diff --git a/frontend/farm_designer/map/__tests__/util_test.ts b/frontend/farm_designer/map/__tests__/util_test.ts
index 39b61cc05..fa0a3e0a0 100644
--- a/frontend/farm_designer/map/__tests__/util_test.ts
+++ b/frontend/farm_designer/map/__tests__/util_test.ts
@@ -4,11 +4,6 @@ jest.mock("../../../history", () => ({
history: { getCurrentLocation: () => ({ pathname: mockPath }) }
}));
-let mockGardenOpen = true;
-jest.mock("../../saved_gardens/saved_gardens", () => ({
- savedGardenOpen: () => mockGardenOpen,
-}));
-
import {
round,
translateScreenToGarden,
@@ -23,6 +18,7 @@ import {
cursorAtPlant,
allowInteraction,
allowGroupAreaInteraction,
+ savedGardenOpen,
} from "../util";
import { McuParams } from "farmbot";
import {
@@ -374,17 +370,22 @@ describe("getMode()", () => {
expect(getMode()).toEqual(Mode.weeds);
mockPath = "/app/designer/weeds/add";
expect(getMode()).toEqual(Mode.createWeed);
- mockPath = "/app/designer/gardens";
- mockGardenOpen = true;
+ mockPath = "/app/designer/gardens/1";
expect(getMode()).toEqual(Mode.templateView);
mockPath = "/app/designer/groups/1";
expect(getMode()).toEqual(Mode.editGroup);
mockPath = "";
- mockGardenOpen = false;
expect(getMode()).toEqual(Mode.none);
});
});
+describe("savedGardenOpen", () => {
+ it("is open", () => {
+ const result = savedGardenOpen(["", "", "", "gardens", "4", ""]);
+ expect(result).toEqual(4);
+ });
+});
+
describe("getGardenCoordinates()", () => {
beforeEach(() => {
Object.defineProperty(document, "querySelector", {
diff --git a/frontend/farm_designer/map/util.ts b/frontend/farm_designer/map/util.ts
index 64f8299f7..8ec9b29d6 100644
--- a/frontend/farm_designer/map/util.ts
+++ b/frontend/farm_designer/map/util.ts
@@ -7,7 +7,6 @@ import {
} from "./interfaces";
import { trim } from "../../util";
import { history, getPathArray } from "../../history";
-import { savedGardenOpen } from "../saved_gardens/saved_gardens";
/*
* Farm Designer Map Utilities
@@ -293,6 +292,7 @@ export const getMode = (): Mode => {
if ((pathArray[3] === "groups" || pathArray[3] === "zones")
&& pathArray[4]) { return Mode.editGroup; }
if (pathArray[6] === "add") { return Mode.clickToAdd; }
+ if (savedGardenOpen(pathArray)) { return Mode.templateView; }
if (!isNaN(parseInt(pathArray.slice(-1)[0]))) { return Mode.editPlant; }
if (pathArray[5] === "edit") { return Mode.editPlant; }
if (pathArray[6] === "edit") { return Mode.editPlant; }
@@ -307,11 +307,15 @@ export const getMode = (): Mode => {
if (pathArray[4] === "add") { return Mode.createWeed; }
return Mode.weeds;
}
- if (savedGardenOpen(pathArray)) { return Mode.templateView; }
}
return Mode.none;
};
+/** Check if a SavedGarden is currently open (URL approach). */
+export const savedGardenOpen = (pathArray: string[]) =>
+ pathArray[3] === "gardens" && parseInt(pathArray[4]) > 0
+ ? parseInt(pathArray[4]) : false;
+
export const getZoomLevelFromMap = (map: Element) =>
parseFloat((window.getComputedStyle(map).transform || "(1").split("(")[1]);
diff --git a/frontend/farm_designer/plants/__tests__/crop_catalog_test.tsx b/frontend/farm_designer/plants/__tests__/crop_catalog_test.tsx
index b4eea09d9..39d6ede51 100644
--- a/frontend/farm_designer/plants/__tests__/crop_catalog_test.tsx
+++ b/frontend/farm_designer/plants/__tests__/crop_catalog_test.tsx
@@ -18,6 +18,7 @@ import { history } from "../../../history";
import {
fakeCropLiveSearchResult,
} from "../../../__test_support__/fake_crop_search_result";
+import { SearchField } from "../../../ui/search_field";
describe("
", () => {
const fakeProps = (): CropCatalogProps => {
@@ -40,9 +41,7 @@ describe("
", () => {
it("handles search term change", () => {
const p = fakeProps();
const wrapper = shallow(
);
- wrapper.find("input").first().simulate("change", {
- currentTarget: { value: "apple" }
- });
+ wrapper.find(SearchField).simulate("change", "apple");
expect(p.dispatch).toHaveBeenCalledWith({
payload: "apple",
type: Actions.SEARCH_QUERY_CHANGE
@@ -69,7 +68,7 @@ describe("
", () => {
p.cropSearchQuery = "abc";
p.cropSearchInProgress = true;
p.cropSearchResults = [fakeCropLiveSearchResult()];
- const wrapper = shallow(
);
- expect(wrapper.find("Spinner").length).toEqual(1);
+ const wrapper = mount(
);
+ expect(wrapper.find(".spinner").length).toEqual(1);
});
});
diff --git a/frontend/farm_designer/plants/__tests__/plant_inventory_test.tsx b/frontend/farm_designer/plants/__tests__/plant_inventory_test.tsx
index 7a53073f6..496bdb96b 100644
--- a/frontend/farm_designer/plants/__tests__/plant_inventory_test.tsx
+++ b/frontend/farm_designer/plants/__tests__/plant_inventory_test.tsx
@@ -9,6 +9,7 @@ import {
import { mount, shallow } from "enzyme";
import { fakePlant } from "../../../__test_support__/fake_state/resources";
import { fakeState } from "../../../__test_support__/fake_state";
+import { SearchField } from "../../../ui/search_field";
describe("
", () => {
const fakeProps = (): PlantInventoryProps => ({
@@ -31,11 +32,10 @@ describe("
", () => {
expect(wrapper.html()).toContain("/app/designer/plants/crop_search");
});
- it("updates search term", () => {
+ it("changes search term", () => {
const wrapper = shallow
();
expect(wrapper.state().searchTerm).toEqual("");
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "mint" } });
+ wrapper.find(SearchField).simulate("change", "mint");
expect(wrapper.state().searchTerm).toEqual("mint");
});
});
diff --git a/frontend/farm_designer/plants/crop_catalog.tsx b/frontend/farm_designer/plants/crop_catalog.tsx
index 7caa51051..cd57d0bf3 100644
--- a/frontend/farm_designer/plants/crop_catalog.tsx
+++ b/frontend/farm_designer/plants/crop_catalog.tsx
@@ -15,6 +15,7 @@ import {
} from "../designer_panel";
import { t } from "../../i18next_wrapper";
import { Panel } from "../panel_header";
+import { SearchField } from "../../ui/search_field";
export function mapStateToProps(props: Everything): CropCatalogProps {
const { cropSearchQuery, cropSearchInProgress, cropSearchResults
@@ -34,8 +35,7 @@ export class RawCropCatalog extends React.Component {
this.props.openfarmSearch(searchTerm)(this.props.dispatch);
}, 500);
- handleChange = (e: React.SyntheticEvent) => {
- const { value } = e.currentTarget;
+ handleChange = (value: string) => {
this.props.dispatch({ type: Actions.SEARCH_QUERY_CHANGE, payload: value });
this.debouncedOFSearch(value);
}
@@ -67,18 +67,14 @@ export class RawCropCatalog extends React.Component {
title={t("Choose a crop")}
backTo={"/app/designer/plants"} />
-
-
- {this.showResultChangeSpinner &&
- }
-
+ : undefined} />
diff --git a/frontend/farm_designer/plants/plant_inventory.tsx b/frontend/farm_designer/plants/plant_inventory.tsx
index a37397abf..214e7c9ff 100644
--- a/frontend/farm_designer/plants/plant_inventory.tsx
+++ b/frontend/farm_designer/plants/plant_inventory.tsx
@@ -13,6 +13,7 @@ import {
DesignerPanel, DesignerPanelContent, DesignerPanelTop,
} from "../designer_panel";
import { t } from "../../i18next_wrapper";
+import { SearchField } from "../../ui/search_field";
export interface PlantInventoryProps {
plants: TaggedPlant[];
@@ -34,12 +35,8 @@ export function mapStateToProps(props: Everything): PlantInventoryProps {
}
export class RawPlants extends React.Component
{
-
state: State = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) =>
- this.setState({ searchTerm: currentTarget.value })
-
render() {
return
@@ -47,8 +44,9 @@ export class RawPlants extends React.Component {
panel={Panel.Plants}
linkTo={"/app/designer/plants/crop_search"}
title={t("Add plant")}>
-
+ this.setState({ searchTerm })} />
", () => {
const fakeProps = (): GroupListPanelProps => {
@@ -55,8 +56,7 @@ describe("", () => {
it("changes search term", () => {
const p = fakeProps();
const wrapper = shallow();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "one" } });
+ wrapper.find(SearchField).simulate("change", "one");
expect(wrapper.state().searchTerm).toEqual("one");
});
diff --git a/frontend/farm_designer/point_groups/criteria/__tests__/component_test.tsx b/frontend/farm_designer/point_groups/criteria/__tests__/component_test.tsx
index dee0acf3f..2641a2b70 100644
--- a/frontend/farm_designer/point_groups/criteria/__tests__/component_test.tsx
+++ b/frontend/farm_designer/point_groups/criteria/__tests__/component_test.tsx
@@ -95,7 +95,7 @@ describe("", () => {
p.pointsSelectedByGroup = [point1, point2, point3];
p.group.body.point_ids = [1];
const wrapper = mount();
- ["1manually selected", "2selected by filters"].map(string =>
+ ["1 manually selected", "2 selected by filters"].map(string =>
expect(wrapper.text()).toContain(string));
});
@@ -108,7 +108,7 @@ describe("", () => {
p.pointsSelectedByGroup = [point1, point2];
p.group.body.point_ids = [];
const wrapper = mount();
- ["0manually selected", "2selected by filters"].map(string =>
+ ["0 manually selected", "2 selected by filters"].map(string =>
expect(wrapper.text()).toContain(string));
});
diff --git a/frontend/farm_designer/point_groups/criteria/component.tsx b/frontend/farm_designer/point_groups/criteria/component.tsx
index acbfed7b7..c93bb5edd 100644
--- a/frontend/farm_designer/point_groups/criteria/component.tsx
+++ b/frontend/farm_designer/point_groups/criteria/component.tsx
@@ -153,10 +153,7 @@ export const GroupPointCountBreakdown =
dispatch={props.dispatch} />;
return
-
- {manualPoints.length}
-
-
{t("manually selected")}
+
{`${manualPoints.length} ${t("manually selected")}`}
{props.iconDisplay && manualPoints.length > 0 &&
@@ -166,10 +163,7 @@ export const GroupPointCountBreakdown =
{props.shouldDisplay(Feature.criteria_groups) &&
-
- {criteriaPoints.length}
-
-
{t("selected by filters")}
+
{`${criteriaPoints.length} ${t("selected by filters")}`}
{props.iconDisplay && criteriaPoints.length > 0 &&
@@ -186,7 +180,7 @@ export const PointTypeSelection = (props: PointTypeSelectionProps) =>
}
- "]]}
selectedItem={noDayCriteria
diff --git a/frontend/farm_designer/point_groups/group_list_panel.tsx b/frontend/farm_designer/point_groups/group_list_panel.tsx
index ce79dea79..86aee4c42 100644
--- a/frontend/farm_designer/point_groups/group_list_panel.tsx
+++ b/frontend/farm_designer/point_groups/group_list_panel.tsx
@@ -16,6 +16,7 @@ import {
import { Content } from "../../constants";
import { selectAllActivePoints } from "../../resources/selectors";
import { createGroup } from "./actions";
+import { SearchField } from "../../ui/search_field";
export interface GroupListPanelProps {
dispatch: Function;
@@ -39,10 +40,6 @@ export class RawGroupListPanel
extends React.Component {
state: State = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) => {
- this.setState({ searchTerm: currentTarget.value });
- }
-
navigate = (id: number) => history.push(`/app/designer/groups/${id}`);
render() {
@@ -52,10 +49,9 @@ export class RawGroupListPanel
panel={Panel.Groups}
onClick={() => this.props.dispatch(createGroup({ pointUuids: [] }))}
title={t("Add group")}>
-
+ this.setState({ searchTerm })} />
/>", () => {
const fakeProps = (): PointsProps => ({
@@ -48,8 +49,7 @@ describe(" />", () => {
p.genericPoints[0].body.name = "point 0";
p.genericPoints[1].body.name = "point 1";
const wrapper = shallow();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "0" } });
+ wrapper.find(SearchField).simulate("change", "0");
expect(wrapper.state().searchTerm).toEqual("0");
});
diff --git a/frontend/farm_designer/points/point_info.tsx b/frontend/farm_designer/points/point_info.tsx
index f71ec2b99..1a8b0a727 100644
--- a/frontend/farm_designer/points/point_info.tsx
+++ b/frontend/farm_designer/points/point_info.tsx
@@ -56,6 +56,7 @@ export class RawEditPoint extends React.Component {
switch (key) {
case "color":
case "created_by":
+ case "removal_method":
case "type":
return ;
diff --git a/frontend/farm_designer/points/point_inventory.tsx b/frontend/farm_designer/points/point_inventory.tsx
index 4412413a8..524eb75ad 100644
--- a/frontend/farm_designer/points/point_inventory.tsx
+++ b/frontend/farm_designer/points/point_inventory.tsx
@@ -13,6 +13,7 @@ import {
import { selectAllGenericPointers } from "../../resources/selectors";
import { TaggedGenericPointer } from "farmbot";
import { t } from "../../i18next_wrapper";
+import { SearchField } from "../../ui/search_field";
export interface PointsProps {
genericPoints: TaggedGenericPointer[];
@@ -37,10 +38,6 @@ export function mapStateToProps(props: Everything): PointsProps {
export class RawPoints extends React.Component {
state: PointsState = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) => {
- this.setState({ searchTerm: currentTarget.value });
- }
-
render() {
return
@@ -48,8 +45,9 @@ export class RawPoints extends React.Component {
panel={Panel.Points}
linkTo={"/app/designer/points/add"}
title={t("Add point")}>
-
+ this.setState({ searchTerm })} />
({ edit: jest.fn() }));
import * as React from "react";
import { mount, shallow } from "enzyme";
import {
- RawSavedGardens as SavedGardens, mapStateToProps,
- SavedGardenHUD, savedGardenOpen,
+ RawSavedGardens as SavedGardens, mapStateToProps, SavedGardenHUD,
} from "../saved_gardens";
import { clickButton } from "../../../__test_support__/helpers";
import {
@@ -31,6 +30,7 @@ import {
import { SavedGardensProps } from "../interfaces";
import { closeSavedGarden } from "../actions";
import { Actions } from "../../../constants";
+import { SearchField } from "../../../ui/search_field";
describe("", () => {
const fakeProps = (): SavedGardensProps => ({
@@ -60,8 +60,7 @@ describe("", () => {
it("changes search term", () => {
const wrapper = shallow();
expect(wrapper.state().searchTerm).toEqual("");
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "spring" } });
+ wrapper.find(SearchField).simulate("change", "spring");
expect(wrapper.state().searchTerm).toEqual("spring");
});
@@ -99,13 +98,6 @@ describe("mapStateToProps()", () => {
});
});
-describe("savedGardenOpen", () => {
- it("is open", () => {
- const result = savedGardenOpen(["", "", "", "gardens", "4", ""]);
- expect(result).toEqual(4);
- });
-});
-
describe("", () => {
it("renders", () => {
const wrapper = mount();
diff --git a/frontend/farm_designer/saved_gardens/saved_gardens.tsx b/frontend/farm_designer/saved_gardens/saved_gardens.tsx
index b33296208..217578275 100644
--- a/frontend/farm_designer/saved_gardens/saved_gardens.tsx
+++ b/frontend/farm_designer/saved_gardens/saved_gardens.tsx
@@ -18,6 +18,7 @@ import {
EmptyStateWrapper, EmptyStateGraphic,
} from "../../ui/empty_state_wrapper";
import { Content } from "../../constants";
+import { SearchField } from "../../ui/search_field";
export const mapStateToProps = (props: Everything): SavedGardensProps => ({
savedGardens: selectAllSavedGardens(props.resources.index),
@@ -35,9 +36,6 @@ export class RawSavedGardens
unselectPlant(this.props.dispatch)();
}
- onChange = (e: React.SyntheticEvent) =>
- this.setState({ searchTerm: e.currentTarget.value });
-
render() {
return
@@ -46,8 +44,9 @@ export class RawSavedGardens
panel={Panel.SavedGardens}
linkTo={"/app/designer/gardens/add"}
title={t("Add garden")}>
-
+ this.setState({ searchTerm })} />
0}
@@ -62,11 +61,6 @@ export class RawSavedGardens
}
}
-/** Check if a SavedGarden is currently open (URL approach). */
-export const savedGardenOpen = (pathArray: string[]) =>
- pathArray[3] === "gardens" && parseInt(pathArray[4]) > 0
- ? parseInt(pathArray[4]) : false;
-
/** Sticky an indicator and actions menu when a SavedGarden is open. */
export const SavedGardenHUD = (props: { dispatch: Function }) =>
diff --git a/frontend/farm_designer/tools/__tests__/index_test.tsx b/frontend/farm_designer/tools/__tests__/index_test.tsx
index 66a735318..c9b524f47 100644
--- a/frontend/farm_designer/tools/__tests__/index_test.tsx
+++ b/frontend/farm_designer/tools/__tests__/index_test.tsx
@@ -34,6 +34,7 @@ import { edit, save } from "../../../api/crud";
import { ToolSelection } from "../tool_slot_edit_components";
import { ToolsProps } from "../interfaces";
import { mapPointClickAction } from "../../map/actions";
+import { SearchField } from "../../../ui/search_field";
describe("
", () => {
const fakeProps = (): ToolsProps => ({
@@ -117,8 +118,7 @@ describe("
", () => {
p.tools[0].body.name = "tool 0";
p.tools[1].body.name = "tool 1";
const wrapper = shallow
();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "0" } });
+ wrapper.find(SearchField).simulate("change", "0");
expect(wrapper.state().searchTerm).toEqual("0");
});
diff --git a/frontend/farm_designer/tools/index.tsx b/frontend/farm_designer/tools/index.tsx
index 36648d70c..c3b061378 100644
--- a/frontend/farm_designer/tools/index.tsx
+++ b/frontend/farm_designer/tools/index.tsx
@@ -29,6 +29,7 @@ import { BotOriginQuadrant } from "../interfaces";
import { mapPointClickAction } from "../map/actions";
import { getMode } from "../map/util";
import { Mode } from "../map/interfaces";
+import { SearchField } from "../../ui/search_field";
const toolStatus = (value: number | undefined): string => {
switch (value) {
@@ -41,10 +42,6 @@ const toolStatus = (value: number | undefined): string => {
export class RawTools extends React.Component {
state: ToolsState = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) => {
- this.setState({ searchTerm: currentTarget.value });
- }
-
getToolName = (toolId: number | undefined): string | undefined => {
const foundTool = this.props.tools.filter(tool => tool.body.id === toolId)[0];
return foundTool ? foundTool.body.name : undefined;
@@ -183,8 +180,9 @@ export class RawTools extends React.Component {
panel={Panel.Tools}
linkTo={!hasTools ? "/app/designer/tools/add" : undefined}
title={!hasTools ? this.strings.titleText : undefined}>
-
+ this.setState({ searchTerm })} />
/>", () => {
const fakeProps = (): WeedsProps => ({
@@ -20,8 +21,7 @@ describe(" />", () => {
it("changes search term", () => {
const wrapper = shallow();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "0" } });
+ wrapper.find(SearchField).simulate("change", "0");
expect(wrapper.state().searchTerm).toEqual("0");
});
diff --git a/frontend/farm_designer/weeds/weeds_inventory.tsx b/frontend/farm_designer/weeds/weeds_inventory.tsx
index 50b3d1c0d..cfd94068f 100644
--- a/frontend/farm_designer/weeds/weeds_inventory.tsx
+++ b/frontend/farm_designer/weeds/weeds_inventory.tsx
@@ -13,6 +13,7 @@ import { t } from "../../i18next_wrapper";
import { TaggedWeedPointer } from "farmbot";
import { selectAllWeedPointers } from "../../resources/selectors";
import { WeedInventoryItem } from "./weed_inventory_item";
+import { SearchField } from "../../ui/search_field";
export interface WeedsProps {
weeds: TaggedWeedPointer[];
@@ -33,10 +34,6 @@ export const mapStateToProps = (props: Everything): WeedsProps => ({
export class RawWeeds extends React.Component {
state: WeedsState = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) => {
- this.setState({ searchTerm: currentTarget.value });
- }
-
render() {
return
@@ -44,8 +41,9 @@ export class RawWeeds extends React.Component {
panel={Panel.Weeds}
linkTo={"/app/designer/weeds/add"}
title={t("Add weed")}>
-
+ this.setState({ searchTerm })} />
/>", () => {
const fakeProps = (): ZonesProps => ({
@@ -30,8 +31,7 @@ describe(" />", () => {
it("changes search term", () => {
const wrapper = shallow();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "0" } });
+ wrapper.find(SearchField).simulate("change", "0");
expect(wrapper.state().searchTerm).toEqual("0");
});
diff --git a/frontend/farm_designer/zones/zones_inventory.tsx b/frontend/farm_designer/zones/zones_inventory.tsx
index 54d049e67..c3d8e7b61 100644
--- a/frontend/farm_designer/zones/zones_inventory.tsx
+++ b/frontend/farm_designer/zones/zones_inventory.tsx
@@ -17,6 +17,7 @@ import {
import { GroupInventoryItem } from "../point_groups/group_inventory_item";
import { history } from "../../history";
import { initSaveGetId } from "../../api/crud";
+import { SearchField } from "../../ui/search_field";
export interface ZonesProps {
dispatch: Function;
@@ -37,10 +38,6 @@ export const mapStateToProps = (props: Everything): ZonesProps => ({
export class RawZones extends React.Component {
state: ZonesState = { searchTerm: "" };
- update = ({ currentTarget }: React.SyntheticEvent) => {
- this.setState({ searchTerm: currentTarget.value });
- }
-
navigate = (id: number) => history.push(`/app/designer/zones/${id}`);
render() {
@@ -53,8 +50,9 @@ export class RawZones extends React.Component {
}))
.then((id: number) => this.navigate(id)).catch(() => { })}
title={t("Add zone")}>
-
+ this.setState({ searchTerm })} />
({
kind: "initial",
@@ -540,9 +541,7 @@ describe("", () => {
it("changes search term", () => {
const p = fakeProps();
const wrapper = shallow();
- wrapper.find("input").simulate("change", {
- currentTarget: { value: "new" }
- });
+ wrapper.find(SearchField).simulate("change", "new");
expect(updateSearchTerm).toHaveBeenCalledWith("new");
});
diff --git a/frontend/folders/component.tsx b/frontend/folders/component.tsx
index a5e3ec9cc..1111ac3c2 100644
--- a/frontend/folders/component.tsx
+++ b/frontend/folders/component.tsx
@@ -46,6 +46,7 @@ import { Content } from "../constants";
import { StepDragger, NULL_DRAGGER_ID } from "../draggable/step_dragger";
import { variableList } from "../sequences/locals_list/variable_support";
import { UUID } from "../resources/interfaces";
+import { SearchField } from "../ui/search_field";
export const FolderListItem = (props: FolderItemProps) => {
const { sequence, movedSequenceUuid } = props;
@@ -328,16 +329,10 @@ export class Folders extends React.Component {
export const FolderPanelTop = (props: FolderPanelTopProps) =>
-
-
-
- updateSearchTerm(e.currentTarget.value)}
- type="text" name="searchTerm"
- placeholder={t("Search sequences...")} />
-
-
+
diff --git a/frontend/logs/__tests__/index_test.tsx b/frontend/logs/__tests__/index_test.tsx
index 66c4d5498..ac5f67230 100644
--- a/frontend/logs/__tests__/index_test.tsx
+++ b/frontend/logs/__tests__/index_test.tsx
@@ -10,6 +10,7 @@ import { fakeLog } from "../../__test_support__/fake_state/resources";
import { LogsProps } from "../interfaces";
import { MessageType } from "../../sequences/interfaces";
import { fakeTimeSettings } from "../../__test_support__/fake_time_settings";
+import { SearchField } from "../../ui/search_field";
describe("
", () => {
function fakeLogs(): TaggedLog[] {
@@ -176,8 +177,7 @@ describe("
", () => {
it("changes search term", () => {
const p = fakeProps();
const wrapper = shallow
();
- wrapper.find("input").first().simulate("change",
- { currentTarget: { value: "one" } });
+ wrapper.find(SearchField).first().simulate("change", "one");
expect(wrapper.state().searchTerm).toEqual("one");
});
});
diff --git a/frontend/logs/index.tsx b/frontend/logs/index.tsx
index 2cbd8e013..506c8a16f 100644
--- a/frontend/logs/index.tsx
+++ b/frontend/logs/index.tsx
@@ -17,6 +17,7 @@ import { NumberConfigKey } from "farmbot/dist/resources/configs/web_app";
import { t } from "../i18next_wrapper";
import { TimeSettings } from "../interfaces";
import { timeFormatString } from "../util";
+import { SearchField } from "../ui/search_field";
/** Format log date and time for display in the app. */
export const formatLogTime =
@@ -125,15 +126,10 @@ export class RawLogs extends React.Component> {
-
-
-
-
- this.setState({ searchTerm: e.currentTarget.value })}
- placeholder={t("Search logs...")} />
-
-
+ this.setState({ searchTerm })} />
diff --git a/frontend/regimens/list/__tests__/index_test.tsx b/frontend/regimens/list/__tests__/index_test.tsx
index c1d84c773..c827b9c8f 100644
--- a/frontend/regimens/list/__tests__/index_test.tsx
+++ b/frontend/regimens/list/__tests__/index_test.tsx
@@ -1,9 +1,8 @@
import * as React from "react";
-import { mount } from "enzyme";
+import { mount, shallow } from "enzyme";
import { RegimensList } from "../index";
import { RegimensListProps } from "../../interfaces";
import { fakeRegimen } from "../../../__test_support__/fake_state/resources";
-import { inputEvent } from "../../../__test_support__/fake_html_events";
describe("", () => {
function fakeProps(): RegimensListProps {
@@ -25,8 +24,8 @@ describe("", () => {
});
it("sets search term", () => {
- const wrapper = mount();
- wrapper.instance().onChange(inputEvent("term"));
+ const wrapper = shallow();
+ wrapper.find("RegimenListHeader").simulate("change", "term");
expect(wrapper.state().searchTerm).toEqual("term");
});
});
diff --git a/frontend/regimens/list/index.tsx b/frontend/regimens/list/index.tsx
index 64c20998b..677bed5e3 100644
--- a/frontend/regimens/list/index.tsx
+++ b/frontend/regimens/list/index.tsx
@@ -7,23 +7,21 @@ import { sortResourcesById } from "../../util";
import { t } from "../../i18next_wrapper";
import { EmptyStateWrapper, EmptyStateGraphic } from "../../ui/empty_state_wrapper";
import { Content } from "../../constants";
+import { SearchField } from "../../ui/search_field";
interface RegimenListHeaderProps {
- onChange(e: React.SyntheticEvent): void;
+ searchTerm: string;
+ onChange(searchTerm: string): void;
regimenCount: number;
dispatch: Function;
}
const RegimenListHeader = (props: RegimenListHeaderProps) =>
;
@@ -55,16 +53,13 @@ export class RegimensList extends
;
}
- onChange = (e: React.SyntheticEvent) => {
- this.setState({ searchTerm: e.currentTarget.value });
- }
-
render() {
return
+ searchTerm={this.state.searchTerm}
+ onChange={searchTerm => this.setState({ searchTerm })} />
0}
diff --git a/frontend/ui/__tests__/search_field_test.tsx b/frontend/ui/__tests__/search_field_test.tsx
new file mode 100644
index 000000000..aeb70072a
--- /dev/null
+++ b/frontend/ui/__tests__/search_field_test.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import { mount, shallow } from "enzyme";
+import { SearchField, SearchFieldProps } from "../search_field";
+import { changeEvent } from "../../__test_support__/fake_html_events";
+
+describe("", () => {
+ const fakeProps = (): SearchFieldProps => ({
+ onChange: jest.fn(),
+ searchTerm: "",
+ placeholder: "search...",
+ });
+
+ it("renders", () => {
+ const wrapper = mount();
+ expect(wrapper.find("input").props().placeholder).toEqual("search...");
+ });
+
+ it("changes search term", () => {
+ const p = fakeProps();
+ const wrapper = shallow();
+ const e = changeEvent("new");
+ wrapper.find("input").simulate("change", e);
+ expect(p.onChange).toHaveBeenCalledWith("new");
+ });
+
+ it("changes search term on key press", () => {
+ const p = fakeProps();
+ p.onKeyPress = jest.fn();
+ const wrapper = shallow();
+ const e = changeEvent("new");
+ wrapper.find("input").simulate("KeyPress", e);
+ expect(p.onKeyPress).toHaveBeenCalledWith("new");
+ });
+
+ it("doesn't change search term on key press", () => {
+ const p = fakeProps();
+ p.onKeyPress = undefined;
+ const wrapper = shallow();
+ const e = changeEvent("new");
+ wrapper.find("input").simulate("KeyPress", e);
+ expect(p.onChange).not.toHaveBeenCalled();
+ });
+});
diff --git a/frontend/ui/search_field.tsx b/frontend/ui/search_field.tsx
new file mode 100644
index 000000000..b87ce9342
--- /dev/null
+++ b/frontend/ui/search_field.tsx
@@ -0,0 +1,30 @@
+import * as React from "react";
+import { ErrorBoundary } from "../error_boundary";
+
+export interface SearchFieldProps {
+ onChange(searchTerm: string): void;
+ onKeyPress?: (searchTerm: string) => void;
+ searchTerm: string;
+ placeholder: string;
+ customLeftIcon?: React.ReactElement;
+ customRightIcon?: React.ReactElement;
+ autoFocus?: boolean;
+}
+
+export const SearchField = (props: SearchFieldProps) =>
+
+
+
+
+ {props.customLeftIcon || }
+ props.onChange(e.currentTarget.value)}
+ onKeyPress={e => props.onKeyPress?.(e.currentTarget.value)}
+ placeholder={props.placeholder} />
+ {props.searchTerm && props.customRightIcon}
+
+
+
+
;