- {newFormat &&
|
}
+ {newFormat &&
|
}
{pinBindings
.sort((a, b) => sortByNameAndPin(a.pin_number, b.pin_number))
.map(x => {
diff --git a/frontend/farm_designer/__tests__/reducer_test.ts b/frontend/farm_designer/__tests__/reducer_test.ts
index 7b733efeb..595de4d80 100644
--- a/frontend/farm_designer/__tests__/reducer_test.ts
+++ b/frontend/farm_designer/__tests__/reducer_test.ts
@@ -191,6 +191,16 @@ describe("designer reducer", () => {
expect(newState.tryGroupSortType).toEqual("random");
});
+ it("sets settings search term", () => {
+ const state = oldState();
+ state.settingsSearchTerm = "";
+ const action: ReduxAction
= {
+ type: Actions.SET_SETTINGS_SEARCH_TERM, payload: "random"
+ };
+ const newState = designer(state, action);
+ expect(newState.settingsSearchTerm).toEqual("random");
+ });
+
it("enables edit group area in map mode", () => {
const state = oldState();
state.editGroupAreaInMap = false;
diff --git a/frontend/farm_designer/__tests__/settings_test.tsx b/frontend/farm_designer/__tests__/settings_test.tsx
deleted file mode 100644
index 82deb77fb..000000000
--- a/frontend/farm_designer/__tests__/settings_test.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-jest.mock("../../config_storage/actions", () => ({
- getWebAppConfigValue: jest.fn(x => { x(); return jest.fn(() => true); }),
- setWebAppConfigValue: jest.fn(),
-}));
-
-import * as React from "react";
-import { mount, ReactWrapper } from "enzyme";
-import {
- RawDesignerSettings as DesignerSettings, DesignerSettingsProps,
- mapStateToProps,
-} from "../settings";
-import { fakeState } from "../../__test_support__/fake_state";
-import { BooleanSetting, NumericSetting } from "../../session_keys";
-import { setWebAppConfigValue } from "../../config_storage/actions";
-
-const getSetting =
- (wrapper: ReactWrapper, position: number, containsString: string) => {
- const setting = wrapper.find(".designer-setting").at(position);
- expect(setting.text().toLowerCase())
- .toContain(containsString.toLowerCase());
- return setting;
- };
-
-describe("", () => {
- const fakeProps = (): DesignerSettingsProps => ({
- dispatch: jest.fn(),
- getConfigValue: jest.fn(),
- });
-
- it("renders settings", () => {
- const wrapper = mount();
- expect(wrapper.text()).toContain("size");
- const settings = wrapper.find(".designer-setting");
- expect(settings.length).toEqual(7);
- });
-
- it("renders defaultOn setting", () => {
- const p = fakeProps();
- p.getConfigValue = () => undefined;
- const wrapper = mount();
- const confirmDeletion = getSetting(wrapper, 6, "confirm plant");
- expect(confirmDeletion.find("button").text()).toEqual("on");
- });
-
- it("toggles setting", () => {
- const wrapper = mount();
- const trailSetting = getSetting(wrapper, 1, "trail");
- trailSetting.find("button").simulate("click");
- expect(setWebAppConfigValue)
- .toHaveBeenCalledWith(BooleanSetting.display_trail, true);
- });
-
- it("changes origin", () => {
- const p = fakeProps();
- p.getConfigValue = () => 2;
- const wrapper = mount();
- const originSetting = getSetting(wrapper, 5, "origin");
- originSetting.find("div").last().simulate("click");
- expect(setWebAppConfigValue).toHaveBeenCalledWith(
- NumericSetting.bot_origin_quadrant, 4);
- });
-});
-
-describe("mapStateToProps()", () => {
- it("returns props", () => {
- const props = mapStateToProps(fakeState());
- const value = props.getConfigValue(BooleanSetting.show_plants);
- expect(value).toEqual(true);
- });
-});
diff --git a/frontend/farm_designer/interfaces.ts b/frontend/farm_designer/interfaces.ts
index 6ebf62f9f..6543c7e10 100644
--- a/frontend/farm_designer/interfaces.ts
+++ b/frontend/farm_designer/interfaces.ts
@@ -125,6 +125,7 @@ export interface DesignerState {
openedSavedGarden: string | undefined;
tryGroupSortType: PointGroupSortType | "nn" | undefined;
editGroupAreaInMap: boolean;
+ settingsSearchTerm: string;
}
export type TaggedExecutable = TaggedSequence | TaggedRegimen;
diff --git a/frontend/farm_designer/reducer.ts b/frontend/farm_designer/reducer.ts
index 81909dee2..02f8a8378 100644
--- a/frontend/farm_designer/reducer.ts
+++ b/frontend/farm_designer/reducer.ts
@@ -27,6 +27,7 @@ export const initialState: DesignerState = {
openedSavedGarden: undefined,
tryGroupSortType: undefined,
editGroupAreaInMap: false,
+ settingsSearchTerm: "",
};
export const designer = generateReducer(initialState)
@@ -107,6 +108,10 @@ export const designer = generateReducer(initialState)
s.tryGroupSortType = payload;
return s;
})
+ .add(Actions.SET_SETTINGS_SEARCH_TERM, (s, { payload }) => {
+ s.settingsSearchTerm = payload;
+ return s;
+ })
.add(Actions.EDIT_GROUP_AREA_IN_MAP, (s, { payload }) => {
s.editGroupAreaInMap = payload;
return s;
diff --git a/frontend/farm_designer/settings.tsx b/frontend/farm_designer/settings.tsx
deleted file mode 100644
index f35516362..000000000
--- a/frontend/farm_designer/settings.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import * as React from "react";
-import { Everything } from "../interfaces";
-import { connect } from "react-redux";
-import { Content } from "../constants";
-import { DesignerPanel, DesignerPanelContent } from "./designer_panel";
-import { t } from "../i18next_wrapper";
-import {
- GetWebAppConfigValue, getWebAppConfigValue, setWebAppConfigValue,
-} from "../config_storage/actions";
-import { Row, Col } from "../ui";
-import { ToggleButton } from "../controls/toggle_button";
-import { BooleanConfigKey } from "farmbot/dist/resources/configs/web_app";
-import { BooleanSetting, NumericSetting } from "../session_keys";
-import { resetVirtualTrail } from "./map/layers/farmbot/bot_trail";
-import { MapSizeInputs } from "./map_size_setting";
-import { DesignerNavTabs, Panel } from "./panel_header";
-import { isUndefined } from "lodash";
-
-export const mapStateToProps = (props: Everything): DesignerSettingsProps => ({
- dispatch: props.dispatch,
- getConfigValue: getWebAppConfigValue(() => props),
-});
-
-export interface DesignerSettingsProps {
- dispatch: Function;
- getConfigValue: GetWebAppConfigValue;
-}
-
-export class RawDesignerSettings
- extends React.Component {
-
- render() {
- const { getConfigValue, dispatch } = this.props;
- const settingsProps = { getConfigValue, dispatch };
- return
-
-
- {DESIGNER_SETTINGS(settingsProps).map(setting =>
- )}
-
- ;
- }
-}
-
-interface SettingDescriptionProps {
- setting?: BooleanConfigKey;
- title: string;
- description: string;
- invert?: boolean;
- callback?: () => void;
- children?: React.ReactChild;
- defaultOn?: boolean;
- disabled?: boolean;
-}
-
-interface SettingProps
- extends DesignerSettingsProps, SettingDescriptionProps { }
-
-const Setting = (props: SettingProps) => {
- const { title, setting, callback, defaultOn } = props;
- const raw_value = setting ? props.getConfigValue(setting) : undefined;
- const value = (defaultOn && isUndefined(raw_value)) ? true : !!raw_value;
- return
-
-
-
-
-
- {setting && {
- props.dispatch(setWebAppConfigValue(setting, !value));
- callback?.();
- }}
- title={`${t("toggle")} ${title}`}
- customText={{ textFalse: t("off"), textTrue: t("on") }} />}
-
-
-
- {t(props.description)}
-
- {props.children}
-
;
-};
-
-const DESIGNER_SETTINGS =
- (settingsProps: DesignerSettingsProps): SettingDescriptionProps[] => ([
- {
- title: t("Display plant animations"),
- description: t(Content.PLANT_ANIMATIONS),
- setting: BooleanSetting.disable_animations,
- invert: true
- },
- {
- title: t("Display virtual FarmBot trail"),
- description: t(Content.VIRTUAL_TRAIL),
- setting: BooleanSetting.display_trail,
- callback: resetVirtualTrail,
- },
- {
- title: t("Dynamic map size"),
- description: t(Content.DYNAMIC_MAP_SIZE),
- setting: BooleanSetting.dynamic_map,
- },
- {
- title: t("Map size"),
- description: t(Content.MAP_SIZE),
- children: ,
- disabled: !!settingsProps.getConfigValue(BooleanSetting.dynamic_map),
- },
- {
- title: t("Rotate map"),
- description: t(Content.MAP_SWAP_XY),
- setting: BooleanSetting.xy_swap,
- },
- {
- title: t("Map origin"),
- description: t(Content.MAP_ORIGIN),
- children:
- },
- {
- title: t("Confirm plant deletion"),
- description: t(Content.CONFIRM_PLANT_DELETION),
- setting: BooleanSetting.confirm_plant_deletion,
- defaultOn: true,
- },
- ]);
-
-const OriginSelector = (props: DesignerSettingsProps) => {
- const quadrant = props.getConfigValue(NumericSetting.bot_origin_quadrant);
- const update = (value: number) => () => props.dispatch(setWebAppConfigValue(
- NumericSetting.bot_origin_quadrant, value));
- return
-
- {[2, 1, 3, 4].map(q =>
-
)}
-
-
;
-};
-
-export const DesignerSettings = connect(mapStateToProps)(RawDesignerSettings);
diff --git a/frontend/farm_designer/settings/__tests__/farm_designer_settings_test.tsx b/frontend/farm_designer/settings/__tests__/farm_designer_settings_test.tsx
new file mode 100644
index 000000000..15d2073ca
--- /dev/null
+++ b/frontend/farm_designer/settings/__tests__/farm_designer_settings_test.tsx
@@ -0,0 +1,35 @@
+jest.mock("../../map/layers/farmbot/bot_trail", () => ({
+ resetVirtualTrail: jest.fn(),
+}));
+
+import * as React from "react";
+import { mount } from "enzyme";
+import { PlainDesignerSettings } from "../farm_designer_settings";
+import { DesignerSettingsPropsBase } from "../interfaces";
+import { resetVirtualTrail } from "../../map/layers/farmbot/bot_trail";
+
+describe("", () => {
+ const fakeProps = (): DesignerSettingsPropsBase => ({
+ dispatch: jest.fn(),
+ getConfigValue: jest.fn(),
+ });
+
+ it("renders", () => {
+ const wrapper = mount({PlainDesignerSettings(fakeProps())}
);
+ expect(wrapper.text().toLowerCase()).toContain("plant animations");
+ });
+
+ it("doesn't call callback", () => {
+ const wrapper = mount({PlainDesignerSettings(fakeProps())}
);
+ expect(wrapper.find("label").at(0).text()).toContain("animations");
+ wrapper.find("button").at(0).simulate("click");
+ expect(resetVirtualTrail).not.toHaveBeenCalled();
+ });
+
+ it("calls callback", () => {
+ const wrapper = mount({PlainDesignerSettings(fakeProps())}
);
+ expect(wrapper.find("label").at(1).text()).toContain("trail");
+ wrapper.find("button").at(1).simulate("click");
+ expect(resetVirtualTrail).toHaveBeenCalled();
+ });
+});
diff --git a/frontend/farm_designer/settings/__tests__/index_test.tsx b/frontend/farm_designer/settings/__tests__/index_test.tsx
new file mode 100644
index 000000000..334653eec
--- /dev/null
+++ b/frontend/farm_designer/settings/__tests__/index_test.tsx
@@ -0,0 +1,172 @@
+jest.mock("../../../config_storage/actions", () => ({
+ getWebAppConfigValue: jest.fn(x => { x(); return jest.fn(() => true); }),
+ setWebAppConfigValue: jest.fn(),
+}));
+
+let mockDev = false;
+jest.mock("../../../account/dev/dev_support", () => ({
+ DevSettings: {
+ futureFeaturesEnabled: () => mockDev,
+ overriddenFbosVersion: jest.fn(),
+ }
+}));
+
+jest.mock("../../../devices/components/maybe_highlight", () => ({
+ maybeOpenPanel: jest.fn(),
+ Highlight: (p: { children: React.ReactChild }) => {p.children}
,
+}));
+
+import * as React from "react";
+import { mount, ReactWrapper, shallow } from "enzyme";
+import { RawDesignerSettings as DesignerSettings } from "..";
+import { DesignerSettingsProps } from "../interfaces";
+import { BooleanSetting, NumericSetting } from "../../../session_keys";
+import { setWebAppConfigValue } from "../../../config_storage/actions";
+import {
+ buildResourceIndex, fakeDevice,
+} from "../../../__test_support__/resource_index_builder";
+import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
+import { bot } from "../../../__test_support__/fake_state/bot";
+import { clickButton } from "../../../__test_support__/helpers";
+import { Actions } from "../../../constants";
+import { Motors } from "../hardware_settings";
+import { SearchField } from "../../../ui/search_field";
+import { maybeOpenPanel } from "../../../devices/components/maybe_highlight";
+
+const getSetting =
+ (wrapper: ReactWrapper, position: number, containsString: string) => {
+ const setting = wrapper.find(".designer-setting").at(position);
+ expect(setting.text().toLowerCase())
+ .toContain(containsString.toLowerCase());
+ return setting;
+ };
+
+describe("", () => {
+ beforeEach(() => {
+ mockDev = false;
+ });
+
+ const fakeProps = (): DesignerSettingsProps => ({
+ dispatch: jest.fn(),
+ getConfigValue: jest.fn(),
+ firmwareConfig: undefined,
+ sourceFwConfig: () => ({ value: 10, consistent: true }),
+ sourceFbosConfig: () => ({ value: 10, consistent: true }),
+ resources: buildResourceIndex().index,
+ deviceAccount: fakeDevice(),
+ env: {},
+ alerts: [],
+ shouldDisplay: jest.fn(),
+ saveFarmwareEnv: jest.fn(),
+ timeSettings: fakeTimeSettings(),
+ bot: bot,
+ searchTerm: "",
+ });
+
+ it("renders settings", () => {
+ const wrapper = mount();
+ expect(wrapper.text()).toContain("size");
+ expect(wrapper.text().toLowerCase()).not.toContain("pin");
+ const settings = wrapper.find(".designer-setting");
+ expect(settings.length).toEqual(7);
+ });
+
+ it("renders all settings", () => {
+ mockDev = true;
+ const wrapper = mount();
+ expect(wrapper.text().toLowerCase()).toContain("pin");
+ });
+
+ it("mounts", () => {
+ mount();
+ expect(maybeOpenPanel).toHaveBeenCalled();
+ });
+
+ it("unmounts", () => {
+ const p = fakeProps();
+ const wrapper = mount();
+ wrapper.unmount();
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.BULK_TOGGLE_CONTROL_PANEL,
+ payload: { open: false, all: true },
+ });
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.TOGGLE_CONTROL_PANEL_OPTION,
+ payload: "farmbot_os",
+ });
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.SET_SETTINGS_SEARCH_TERM,
+ payload: "",
+ });
+ });
+
+ it("changes search term", () => {
+ const p = fakeProps();
+ const wrapper = shallow();
+ wrapper.find(SearchField).simulate("change", "setting");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.BULK_TOGGLE_CONTROL_PANEL,
+ payload: { open: true, all: true },
+ });
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.SET_SETTINGS_SEARCH_TERM,
+ payload: "setting",
+ });
+ });
+
+ it("fetches firmware_hardware", () => {
+ mockDev = true;
+ const p = fakeProps();
+ p.sourceFbosConfig = () => ({ value: "arduino", consistent: true });
+ const wrapper = mount();
+ expect(wrapper.find(Motors).props().firmwareHardware).toEqual("arduino");
+ });
+
+ it("expands all", () => {
+ mockDev = true;
+ const p = fakeProps();
+ const wrapper = mount();
+ clickButton(wrapper, 0, "expand all");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.BULK_TOGGLE_CONTROL_PANEL,
+ payload: { open: true, all: true },
+ });
+ });
+
+ it("collapses all", () => {
+ mockDev = true;
+ const p = fakeProps();
+ const wrapper = mount();
+ clickButton(wrapper, 1, "collapse all");
+ expect(p.dispatch).toHaveBeenCalledWith({
+ type: Actions.BULK_TOGGLE_CONTROL_PANEL,
+ payload: { open: false, all: true },
+ });
+ });
+
+ it("renders defaultOn setting", () => {
+ const p = fakeProps();
+ p.getConfigValue = () => undefined;
+ const wrapper = mount();
+ const confirmDeletion = getSetting(wrapper, 6, "confirm plant");
+ expect(confirmDeletion.find("button").text()).toEqual("on");
+ });
+
+ it("toggles setting", () => {
+ const wrapper = mount();
+ const trailSetting = getSetting(wrapper, 1, "trail");
+ trailSetting.find("button").simulate("click");
+ expect(setWebAppConfigValue)
+ .toHaveBeenCalledWith(BooleanSetting.display_trail, true);
+ });
+
+ it("changes origin", () => {
+ const p = fakeProps();
+ p.getConfigValue = () => 2;
+ const wrapper = mount();
+ const originSetting = getSetting(wrapper, 5, "origin");
+ originSetting.find("div").last().simulate("click");
+ expect(setWebAppConfigValue).toHaveBeenCalledWith(
+ NumericSetting.bot_origin_quadrant, 4);
+ });
+});
diff --git a/frontend/farm_designer/settings/__tests__/state_to_props_test.ts b/frontend/farm_designer/settings/__tests__/state_to_props_test.ts
new file mode 100644
index 000000000..9bc5e920a
--- /dev/null
+++ b/frontend/farm_designer/settings/__tests__/state_to_props_test.ts
@@ -0,0 +1,16 @@
+jest.mock("../../../config_storage/actions", () => ({
+ getWebAppConfigValue: jest.fn(x => { x(); return jest.fn(() => true); }),
+ setWebAppConfigValue: jest.fn(),
+}));
+
+import { mapStateToProps } from "../state_to_props";
+import { fakeState } from "../../../__test_support__/fake_state";
+import { BooleanSetting } from "../../../session_keys";
+
+describe("mapStateToProps()", () => {
+ it("returns props", () => {
+ const props = mapStateToProps(fakeState());
+ const value = props.getConfigValue(BooleanSetting.show_plants);
+ expect(value).toEqual(true);
+ });
+});
diff --git a/frontend/farm_designer/settings/farm_designer_settings.tsx b/frontend/farm_designer/settings/farm_designer_settings.tsx
new file mode 100644
index 000000000..c050b793d
--- /dev/null
+++ b/frontend/farm_designer/settings/farm_designer_settings.tsx
@@ -0,0 +1,125 @@
+import * as React from "react";
+import { Content, DeviceSetting } from "../../constants";
+import { t } from "../../i18next_wrapper";
+import { setWebAppConfigValue } from "../../config_storage/actions";
+import { Row, Col } from "../../ui";
+import { ToggleButton } from "../../controls/toggle_button";
+import { BooleanSetting, NumericSetting } from "../../session_keys";
+import { resetVirtualTrail } from "../map/layers/farmbot/bot_trail";
+import { MapSizeInputs } from "../map_size_setting";
+import { isUndefined } from "lodash";
+import { Collapse } from "@blueprintjs/core";
+import { Header } from "../../devices/components/hardware_settings/header";
+import { Highlight } from "../../devices/components/maybe_highlight";
+import {
+ DesignerSettingsSectionProps, SettingProps,
+ DesignerSettingsPropsBase, SettingDescriptionProps,
+} from "./interfaces";
+
+export const Designer = (props: DesignerSettingsSectionProps) => {
+ const { getConfigValue, dispatch, controlPanelState } = props;
+ const settingsProps = { getConfigValue, dispatch };
+ return
+
+
+ {PlainDesignerSettings(settingsProps)}
+
+ ;
+};
+
+export const PlainDesignerSettings =
+ (settingsProps: DesignerSettingsPropsBase) =>
+ DESIGNER_SETTINGS(settingsProps).map(setting =>
+ );
+
+const Setting = (props: SettingProps) => {
+ const { title, setting, callback, defaultOn } = props;
+ const raw_value = setting ? props.getConfigValue(setting) : undefined;
+ const value = (defaultOn && isUndefined(raw_value)) ? true : !!raw_value;
+ return
+
+
+
+
+
+
+ {setting && {
+ props.dispatch(setWebAppConfigValue(setting, !value));
+ callback?.();
+ }}
+ title={`${t("toggle")} ${title}`}
+ customText={{ textFalse: t("off"), textTrue: t("on") }} />}
+
+
+
+ {t(props.description)}
+
+ {props.children}
+
+ ;
+};
+
+const DESIGNER_SETTINGS =
+ (settingsProps: DesignerSettingsPropsBase): SettingDescriptionProps[] => ([
+ {
+ title: DeviceSetting.animations,
+ description: t(Content.PLANT_ANIMATIONS),
+ setting: BooleanSetting.disable_animations,
+ invert: true
+ },
+ {
+ title: DeviceSetting.trail,
+ description: t(Content.VIRTUAL_TRAIL),
+ setting: BooleanSetting.display_trail,
+ callback: resetVirtualTrail,
+ },
+ {
+ title: DeviceSetting.dynamicMap,
+ description: t(Content.DYNAMIC_MAP_SIZE),
+ setting: BooleanSetting.dynamic_map,
+ },
+ {
+ title: DeviceSetting.mapSize,
+ description: t(Content.MAP_SIZE),
+ children: ,
+ disabled: !!settingsProps.getConfigValue(BooleanSetting.dynamic_map),
+ },
+ {
+ title: DeviceSetting.rotateMap,
+ description: t(Content.MAP_SWAP_XY),
+ setting: BooleanSetting.xy_swap,
+ },
+ {
+ title: DeviceSetting.mapOrigin,
+ description: t(Content.MAP_ORIGIN),
+ children:
+ },
+ {
+ title: DeviceSetting.confirmPlantDeletion,
+ description: t(Content.CONFIRM_PLANT_DELETION),
+ setting: BooleanSetting.confirm_plant_deletion,
+ defaultOn: true,
+ },
+ ]);
+
+const OriginSelector = (props: DesignerSettingsPropsBase) => {
+ const quadrant = props.getConfigValue(NumericSetting.bot_origin_quadrant);
+ const update = (value: number) => () => props.dispatch(setWebAppConfigValue(
+ NumericSetting.bot_origin_quadrant, value));
+ return
+
+ {[2, 1, 3, 4].map(q =>
+
)}
+
+
;
+};
diff --git a/frontend/farm_designer/settings/fbos_settings.ts b/frontend/farm_designer/settings/fbos_settings.ts
new file mode 100644
index 000000000..65152600c
--- /dev/null
+++ b/frontend/farm_designer/settings/fbos_settings.ts
@@ -0,0 +1,3 @@
+export * from "../../devices/components/fbos_settings/power_and_reset";
+export * from "../../devices/components/fbos_settings/firmware";
+export * from "../../devices/components/farmbot_os_settings";
diff --git a/frontend/farm_designer/settings/hardware_settings.ts b/frontend/farm_designer/settings/hardware_settings.ts
new file mode 100644
index 000000000..deb7a46e7
--- /dev/null
+++ b/frontend/farm_designer/settings/hardware_settings.ts
@@ -0,0 +1,9 @@
+export * from "../../devices/components/hardware_settings/homing_and_calibration";
+export * from "../../devices/components/hardware_settings/motors";
+export * from "../../devices/components/hardware_settings/encoders";
+export * from "../../devices/components/hardware_settings/endstops";
+export * from "../../devices/components/hardware_settings/error_handling";
+export * from "../../devices/components/hardware_settings/pin_bindings";
+export * from "../../devices/components/hardware_settings/pin_guard";
+export * from "../../devices/components/hardware_settings/danger_zone";
+export * from "../../devices/components/firmware_hardware_support";
diff --git a/frontend/farm_designer/settings/index.tsx b/frontend/farm_designer/settings/index.tsx
new file mode 100644
index 000000000..6b69f9f92
--- /dev/null
+++ b/frontend/farm_designer/settings/index.tsx
@@ -0,0 +1,135 @@
+import * as React from "react";
+import { connect } from "react-redux";
+import { DesignerPanel, DesignerPanelContent } from "../designer_panel";
+import { t } from "../../i18next_wrapper";
+import { DesignerNavTabs, Panel } from "../panel_header";
+import {
+ bulkToggleControlPanel, MCUFactoryReset, toggleControlPanel,
+} from "../../devices/actions";
+import { FarmBotSettings, Firmware, PowerAndReset } from "./fbos_settings";
+import {
+ HomingAndCalibration, Motors, Encoders, EndStops, ErrorHandling,
+ PinGuard, DangerZone, PinBindings, isFwHardwareValue,
+} from "./hardware_settings";
+import { DevSettings } from "../../account/dev/dev_support";
+import { maybeOpenPanel } from "../../devices/components/maybe_highlight";
+import { isBotOnlineFromState } from "../../devices/must_be_online";
+import { DesignerSettingsProps } from "./interfaces";
+import { Designer, PlainDesignerSettings } from "./farm_designer_settings";
+import { SearchField } from "../../ui/search_field";
+import { mapStateToProps } from "./state_to_props";
+import { Actions } from "../../constants";
+
+export class RawDesignerSettings
+ extends React.Component {
+
+ componentDidMount = () =>
+ this.props.dispatch(maybeOpenPanel(this.props.bot.controlPanelState, true));
+
+ componentWillUnmount = () => {
+ this.props.dispatch(bulkToggleControlPanel(false, true));
+ this.props.dispatch(toggleControlPanel("farmbot_os"));
+ this.props.dispatch({
+ type: Actions.SET_SETTINGS_SEARCH_TERM,
+ payload: ""
+ });
+ }
+
+ render() {
+ const { getConfigValue, dispatch, firmwareConfig,
+ sourceFwConfig, sourceFbosConfig, resources,
+ } = this.props;
+ const { controlPanelState } = this.props.bot;
+ const settingsProps = { getConfigValue, dispatch };
+ const commonProps = { dispatch, controlPanelState };
+ const { value } = this.props.sourceFbosConfig("firmware_hardware");
+ const firmwareHardware = isFwHardwareValue(value) ? value : undefined;
+ const botOnline = isBotOnlineFromState(this.props.bot);
+ return
+
+
+ {
+ dispatch(bulkToggleControlPanel(true, true));
+ dispatch({
+ type: Actions.SET_SETTINGS_SEARCH_TERM,
+ payload: searchTerm
+ });
+ }} />
+ {DevSettings.futureFeaturesEnabled() ?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ :
+ {PlainDesignerSettings(settingsProps)}
+
}
+
+ ;
+ }
+}
+
+export const DesignerSettings = connect(mapStateToProps)(RawDesignerSettings);
diff --git a/frontend/farm_designer/settings/interfaces.ts b/frontend/farm_designer/settings/interfaces.ts
new file mode 100644
index 000000000..3123408ef
--- /dev/null
+++ b/frontend/farm_designer/settings/interfaces.ts
@@ -0,0 +1,53 @@
+import { GetWebAppConfigValue } from "../../config_storage/actions";
+import { FirmwareConfig } from "farmbot/dist/resources/configs/firmware";
+import {
+ SourceFwConfig, SourceFbosConfig, UserEnv, ShouldDisplay,
+ SaveFarmwareEnv, BotState, ControlPanelState,
+} from "../../devices/interfaces";
+import { ResourceIndex } from "../../resources/interfaces";
+import { TaggedDevice, Alert } from "farmbot";
+import { TimeSettings } from "../../interfaces";
+import { DeviceSetting } from "../../constants";
+import {
+ BooleanConfigKey as WebAppBooleanConfigKey,
+} from "farmbot/dist/resources/configs/web_app";
+
+export interface DesignerSettingsPropsBase {
+ dispatch: Function;
+ getConfigValue: GetWebAppConfigValue;
+}
+
+export interface DesignerSettingsProps extends DesignerSettingsPropsBase {
+ firmwareConfig: FirmwareConfig | undefined;
+ sourceFwConfig: SourceFwConfig;
+ sourceFbosConfig: SourceFbosConfig;
+ resources: ResourceIndex;
+ deviceAccount: TaggedDevice;
+ env: UserEnv;
+ alerts: Alert[];
+ shouldDisplay: ShouldDisplay;
+ saveFarmwareEnv: SaveFarmwareEnv;
+ timeSettings: TimeSettings;
+ bot: BotState;
+ searchTerm: string;
+}
+
+export interface DesignerSettingsSectionProps {
+ dispatch: Function;
+ controlPanelState: ControlPanelState;
+ getConfigValue: GetWebAppConfigValue;
+}
+
+export interface SettingDescriptionProps {
+ setting?: WebAppBooleanConfigKey;
+ title: DeviceSetting;
+ description: string;
+ invert?: boolean;
+ callback?: () => void;
+ children?: React.ReactChild;
+ defaultOn?: boolean;
+ disabled?: boolean;
+}
+
+export interface SettingProps
+ extends DesignerSettingsPropsBase, SettingDescriptionProps { }
diff --git a/frontend/farm_designer/settings/state_to_props.ts b/frontend/farm_designer/settings/state_to_props.ts
new file mode 100644
index 000000000..4c0742d2e
--- /dev/null
+++ b/frontend/farm_designer/settings/state_to_props.ts
@@ -0,0 +1,35 @@
+import { Everything } from "../../interfaces";
+import { getWebAppConfigValue } from "../../config_storage/actions";
+import { validFwConfig, validFbosConfig } from "../../util";
+import { getFirmwareConfig, getFbosConfig } from "../../resources/getters";
+import {
+ sourceFwConfigValue, sourceFbosConfigValue,
+} from "../../devices/components/source_config_value";
+import {
+ getDeviceAccountSettings, maybeGetTimeSettings,
+} from "../../resources/selectors";
+import {
+ saveOrEditFarmwareEnv, getShouldDisplayFn, getEnv,
+} from "../../farmware/state_to_props";
+import { getAllAlerts } from "../../messages/state_to_props";
+import { DesignerSettingsProps } from "./interfaces";
+
+export const mapStateToProps = (props: Everything): DesignerSettingsProps => ({
+ dispatch: props.dispatch,
+ getConfigValue: getWebAppConfigValue(() => props),
+ firmwareConfig: validFwConfig(getFirmwareConfig(props.resources.index)),
+ sourceFwConfig: sourceFwConfigValue(validFwConfig(getFirmwareConfig(
+ props.resources.index)), props.bot.hardware.mcu_params),
+ sourceFbosConfig: sourceFbosConfigValue(validFbosConfig(getFbosConfig(
+ props.resources.index)), props.bot.hardware.configuration),
+ resources: props.resources.index,
+ deviceAccount: getDeviceAccountSettings(props.resources.index),
+ shouldDisplay: getShouldDisplayFn(props.resources.index, props.bot),
+ env: getEnv(props.resources.index, getShouldDisplayFn(
+ props.resources.index, props.bot), props.bot),
+ saveFarmwareEnv: saveOrEditFarmwareEnv(props.resources.index),
+ timeSettings: maybeGetTimeSettings(props.resources.index),
+ alerts: getAllAlerts(props.resources),
+ bot: props.bot,
+ searchTerm: props.resources.consumers.farm_designer.settingsSearchTerm,
+});
diff --git a/frontend/ui/__tests__/search_field_test.tsx b/frontend/ui/__tests__/search_field_test.tsx
index aeb70072a..43f2823cc 100644
--- a/frontend/ui/__tests__/search_field_test.tsx
+++ b/frontend/ui/__tests__/search_field_test.tsx
@@ -40,4 +40,12 @@ describe("", () => {
wrapper.find("input").simulate("KeyPress", e);
expect(p.onChange).not.toHaveBeenCalled();
});
+
+ it("clears search term", () => {
+ const p = fakeProps();
+ p.searchTerm = "old";
+ const wrapper = shallow();
+ wrapper.find("i").last().simulate("click");
+ expect(p.onChange).toHaveBeenCalledWith("");
+ });
});
diff --git a/frontend/ui/search_field.tsx b/frontend/ui/search_field.tsx
index b87ce9342..cd84567b7 100644
--- a/frontend/ui/search_field.tsx
+++ b/frontend/ui/search_field.tsx
@@ -23,7 +23,8 @@ export const SearchField = (props: SearchFieldProps) =>
onChange={e => props.onChange(e.currentTarget.value)}
onKeyPress={e => props.onKeyPress?.(e.currentTarget.value)}
placeholder={props.placeholder} />
- {props.searchTerm && props.customRightIcon}
+ {props.searchTerm && (props.customRightIcon ||
+ props.onChange("")} />)}