refactor search fields
parent
73422eb8ea
commit
3c3b120b9b
|
@ -120,46 +120,57 @@
|
|||
|
||||
.thin-search-wrapper {
|
||||
width: 100%;
|
||||
.text-input-wrapper {
|
||||
position: relative;
|
||||
margin: 1rem;
|
||||
border-bottom: 1px solid $dark_gray;
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
.thin-search {
|
||||
.spinner-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: $dark_gray;
|
||||
width: 1px;
|
||||
height: 3px;
|
||||
}
|
||||
&:before {
|
||||
left: 0;
|
||||
}
|
||||
&:after {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
.text-input-wrapper {
|
||||
position: relative;
|
||||
margin: 1rem;
|
||||
border-bottom: 1px solid $dark_gray;
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: $dark_gray;
|
||||
width: 1px;
|
||||
height: 3px;
|
||||
}
|
||||
&:before {
|
||||
left: 0;
|
||||
}
|
||||
&:after {
|
||||
right: 0;
|
||||
}
|
||||
i {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.fa-search {
|
||||
position: absolute;
|
||||
top: 0.8rem;
|
||||
left: 1rem;
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
.fa-search {
|
||||
position: absolute;
|
||||
top: 0.8rem;
|
||||
left: 1rem;
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
input {
|
||||
background: transparent;
|
||||
box-shadow: none !important;
|
||||
padding-left: 3rem !important;
|
||||
font-size: 1.4rem !important;
|
||||
&:active,
|
||||
&:focus {
|
||||
background: transparent !important;
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: $placeholder_gray;
|
||||
input {
|
||||
background: transparent;
|
||||
box-shadow: none !important;
|
||||
padding-left: 3rem !important;
|
||||
font-size: 1.4rem !important;
|
||||
&:active,
|
||||
&:focus {
|
||||
background: transparent !important;
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: $placeholder_gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,18 +300,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thin-search {
|
||||
.spinner-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
padding: 0;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hovered-plant-copy {
|
||||
cursor: pointer;
|
||||
transform-origin: center;
|
||||
|
|
|
@ -925,6 +925,10 @@
|
|||
display: inline;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 50%;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
.point-type-checkboxes {
|
||||
.point-type-section {
|
||||
|
@ -1043,6 +1047,11 @@
|
|||
margin-left: 1rem;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.filter-search {
|
||||
.bp3-popover-wrapper {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.row {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
@ -1074,7 +1083,7 @@
|
|||
}
|
||||
p {
|
||||
display: inline;
|
||||
margin-left: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,21 +81,12 @@ interface DesignerPanelTopProps {
|
|||
onClick?(): void;
|
||||
title?: string;
|
||||
children?: React.ReactNode;
|
||||
noIcon?: boolean;
|
||||
}
|
||||
|
||||
export const DesignerPanelTop = (props: DesignerPanelTopProps) => {
|
||||
const withBtn = !!props.linkTo || !!props.onClick;
|
||||
return <div className={`panel-top ${withBtn ? "with-button" : ""}`}>
|
||||
<div className="thin-search-wrapper">
|
||||
<div className="text-input-wrapper">
|
||||
{!props.noIcon &&
|
||||
<i className="fa fa-search" />}
|
||||
<ErrorBoundary>
|
||||
{props.children}
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
{props.children}
|
||||
{props.onClick &&
|
||||
<a>
|
||||
<div className={`fb-button panel-${TAB_COLOR[props.panel]}`}
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from "../../ui/empty_state_wrapper";
|
||||
import { some, uniq, map, sortBy } from "lodash";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { SearchField } from "../../ui/search_field";
|
||||
|
||||
const filterSearch = (term: string) => (item: CalendarOccurrence) =>
|
||||
item.heading.toLowerCase().includes(term)
|
||||
|
@ -105,14 +106,12 @@ export class PureFarmEvents
|
|||
<DesignerPanelTop
|
||||
panel={Panel.FarmEvents}
|
||||
linkTo={"/app/designer/events/add"}
|
||||
title={t("Add event")}
|
||||
noIcon={true}>
|
||||
<i className="fa fa-calendar" onClick={this.resetCalendar} />
|
||||
<input
|
||||
name="searchTerm"
|
||||
value={this.state.searchTerm}
|
||||
onChange={e => this.setState({ searchTerm: e.currentTarget.value })}
|
||||
placeholder={t("Search your events...")} />
|
||||
title={t("Add event")}>
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
customLeftIcon={
|
||||
<i className="fa fa-calendar" onClick={this.resetCalendar} />}
|
||||
placeholder={t("Search your events...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"farm-event"}>
|
||||
<div className="farm-events">
|
||||
|
|
|
@ -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", {
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { history } from "../../../history";
|
|||
import {
|
||||
fakeCropLiveSearchResult,
|
||||
} from "../../../__test_support__/fake_crop_search_result";
|
||||
import { SearchField } from "../../../ui/search_field";
|
||||
|
||||
describe("<CropCatalog />", () => {
|
||||
const fakeProps = (): CropCatalogProps => {
|
||||
|
@ -40,9 +41,7 @@ describe("<CropCatalog />", () => {
|
|||
it("handles search term change", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<CropCatalog {...p} />);
|
||||
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("<CropCatalog />", () => {
|
|||
p.cropSearchQuery = "abc";
|
||||
p.cropSearchInProgress = true;
|
||||
p.cropSearchResults = [fakeCropLiveSearchResult()];
|
||||
const wrapper = shallow(<CropCatalog {...p} />);
|
||||
expect(wrapper.find("Spinner").length).toEqual(1);
|
||||
const wrapper = mount(<CropCatalog {...p} />);
|
||||
expect(wrapper.find(".spinner").length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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("<PlantInventory />", () => {
|
||||
const fakeProps = (): PlantInventoryProps => ({
|
||||
|
@ -31,11 +32,10 @@ describe("<PlantInventory />", () => {
|
|||
expect(wrapper.html()).toContain("/app/designer/plants/crop_search");
|
||||
});
|
||||
|
||||
it("updates search term", () => {
|
||||
it("changes search term", () => {
|
||||
const wrapper = shallow<Plants>(<Plants {...fakeProps()} />);
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<CropCatalogProps, {}> {
|
|||
this.props.openfarmSearch(searchTerm)(this.props.dispatch);
|
||||
}, 500);
|
||||
|
||||
handleChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
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<CropCatalogProps, {}> {
|
|||
title={t("Choose a crop")}
|
||||
backTo={"/app/designer/plants"} />
|
||||
<DesignerPanelTop panel={Panel.Plants}>
|
||||
<div className="thin-search">
|
||||
<input
|
||||
autoFocus={true}
|
||||
value={this.props.cropSearchQuery}
|
||||
onChange={this.handleChange}
|
||||
onKeyPress={this.handleChange}
|
||||
className="search"
|
||||
name="searchTerm"
|
||||
placeholder={t("Search OpenFarm...")} />
|
||||
{this.showResultChangeSpinner &&
|
||||
<Spinner radius={10} strokeWidth={3} />}
|
||||
</div>
|
||||
<SearchField
|
||||
searchTerm={this.props.cropSearchQuery}
|
||||
placeholder={t("Search OpenFarm...")}
|
||||
onChange={this.handleChange}
|
||||
onKeyPress={this.handleChange}
|
||||
autoFocus={true}
|
||||
customRightIcon={this.showResultChangeSpinner ?
|
||||
<Spinner radius={10} strokeWidth={3} /> : undefined} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"crop-catalog"}>
|
||||
<div className="crop-search-result-wrapper row">
|
||||
|
|
|
@ -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<PlantInventoryProps, State> {
|
||||
|
||||
state: State = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) =>
|
||||
this.setState({ searchTerm: currentTarget.value })
|
||||
|
||||
render() {
|
||||
return <DesignerPanel panelName={"plant-inventory"} panel={Panel.Plants}>
|
||||
<DesignerNavTabs />
|
||||
|
@ -47,8 +44,9 @@ export class RawPlants extends React.Component<PlantInventoryProps, State> {
|
|||
panel={Panel.Plants}
|
||||
linkTo={"/app/designer/plants/crop_search"}
|
||||
title={t("Add plant")}>
|
||||
<input type="text" onChange={this.update} name="searchTerm"
|
||||
placeholder={t("Search your plants...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your plants...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"plant"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from "../../../__test_support__/resource_index_builder";
|
||||
import { createGroup } from "../actions";
|
||||
import { DesignerPanelTop } from "../../designer_panel";
|
||||
import { SearchField } from "../../../ui/search_field";
|
||||
|
||||
describe("<GroupListPanel />", () => {
|
||||
const fakeProps = (): GroupListPanelProps => {
|
||||
|
@ -55,8 +56,7 @@ describe("<GroupListPanel />", () => {
|
|||
it("changes search term", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow<GroupListPanel>(<GroupListPanel {...p} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "one" } });
|
||||
wrapper.find(SearchField).simulate("change", "one");
|
||||
expect(wrapper.state().searchTerm).toEqual("one");
|
||||
});
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ describe("<GroupPointCountBreakdown />", () => {
|
|||
p.pointsSelectedByGroup = [point1, point2, point3];
|
||||
p.group.body.point_ids = [1];
|
||||
const wrapper = mount(<GroupPointCountBreakdown {...p} />);
|
||||
["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("<GroupPointCountBreakdown />", () => {
|
|||
p.pointsSelectedByGroup = [point1, point2];
|
||||
p.group.body.point_ids = [];
|
||||
const wrapper = mount(<GroupPointCountBreakdown {...p} />);
|
||||
["0manually selected", "2selected by filters"].map(string =>
|
||||
["0 manually selected", "2 selected by filters"].map(string =>
|
||||
expect(wrapper.text()).toContain(string));
|
||||
});
|
||||
|
||||
|
|
|
@ -153,10 +153,7 @@ export const GroupPointCountBreakdown =
|
|||
dispatch={props.dispatch} />;
|
||||
return <div className={"group-member-count-breakdown"}>
|
||||
<div className={"manual-group-member-count"}>
|
||||
<div className={"manual-selection-count"}>
|
||||
{manualPoints.length}
|
||||
</div>
|
||||
<p>{t("manually selected")}</p>
|
||||
<p>{`${manualPoints.length} ${t("manually selected")}`}</p>
|
||||
<ClearPointIds dispatch={props.dispatch} group={props.group} />
|
||||
</div>
|
||||
{props.iconDisplay && manualPoints.length > 0 &&
|
||||
|
@ -166,10 +163,7 @@ export const GroupPointCountBreakdown =
|
|||
{props.shouldDisplay(Feature.criteria_groups) &&
|
||||
<div className={"group-member-section"}>
|
||||
<div className={"criteria-group-member-count"}>
|
||||
<div className={"criteria-selection-count"}>
|
||||
{criteriaPoints.length}
|
||||
</div>
|
||||
<p>{t("selected by filters")}</p>
|
||||
<p>{`${criteriaPoints.length} ${t("selected by filters")}`}</p>
|
||||
<ClearCriteria dispatch={props.dispatch} group={props.group} />
|
||||
</div>
|
||||
{props.iconDisplay && criteriaPoints.length > 0 &&
|
||||
|
@ -186,7 +180,7 @@ export const PointTypeSelection = (props: PointTypeSelectionProps) =>
|
|||
<div className={"point-type-selection"}>
|
||||
<p className={"category"}>{t("Select all")}</p>
|
||||
<FBSelect
|
||||
key={JSON.stringify(props.group.body.criteria)}
|
||||
key={JSON.stringify(props.group.body)}
|
||||
list={POINTER_TYPE_LIST().slice(0, -1)}
|
||||
customNullLabel={t("Select one")}
|
||||
selectedItem={props.pointTypes[0]
|
||||
|
|
|
@ -117,7 +117,7 @@ export const DaySelection = (props: DaySelectionProps) => {
|
|||
</div>}
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<FBSelect key={JSON.stringify(criteria)}
|
||||
<FBSelect key={JSON.stringify(group.body)}
|
||||
list={[DAY_OPERATOR_DDI_LOOKUP()["<"],
|
||||
DAY_OPERATOR_DDI_LOOKUP()[">"]]}
|
||||
selectedItem={noDayCriteria
|
||||
|
|
|
@ -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<GroupListPanelProps, State> {
|
||||
state: State = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
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")}>
|
||||
<input type="text"
|
||||
name="searchTerm"
|
||||
onChange={this.update}
|
||||
placeholder={t("Search your groups...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your groups...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"groups"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
buildResourceIndex,
|
||||
} from "../../../__test_support__/resource_index_builder";
|
||||
import { mapStateToProps } from "../point_inventory";
|
||||
import { SearchField } from "../../../ui/search_field";
|
||||
|
||||
describe("<Points> />", () => {
|
||||
const fakeProps = (): PointsProps => ({
|
||||
|
@ -48,8 +49,7 @@ describe("<Points> />", () => {
|
|||
p.genericPoints[0].body.name = "point 0";
|
||||
p.genericPoints[1].body.name = "point 1";
|
||||
const wrapper = shallow<Points>(<Points {...p} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "0" } });
|
||||
wrapper.find(SearchField).simulate("change", "0");
|
||||
expect(wrapper.state().searchTerm).toEqual("0");
|
||||
});
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ export class RawEditPoint extends React.Component<EditPointProps, {}> {
|
|||
switch (key) {
|
||||
case "color":
|
||||
case "created_by":
|
||||
case "removal_method":
|
||||
case "type":
|
||||
return <div key={key}
|
||||
className={`meta-${key}-not-displayed`} />;
|
||||
|
|
|
@ -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<PointsProps, PointsState> {
|
||||
state: PointsState = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
this.setState({ searchTerm: currentTarget.value });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <DesignerPanel panelName={"point-inventory"} panel={Panel.Points}>
|
||||
<DesignerNavTabs />
|
||||
|
@ -48,8 +45,9 @@ export class RawPoints extends React.Component<PointsProps, PointsState> {
|
|||
panel={Panel.Points}
|
||||
linkTo={"/app/designer/points/add"}
|
||||
title={t("Add point")}>
|
||||
<input type="text" onChange={this.update} name="searchTerm"
|
||||
placeholder={t("Search your points...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your points...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"points"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -16,8 +16,7 @@ jest.mock("../../../api/crud", () => ({ 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("<SavedGardens />", () => {
|
||||
const fakeProps = (): SavedGardensProps => ({
|
||||
|
@ -60,8 +60,7 @@ describe("<SavedGardens />", () => {
|
|||
it("changes search term", () => {
|
||||
const wrapper = shallow<SavedGardens>(<SavedGardens {...fakeProps()} />);
|
||||
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("<SavedGardenHUD />", () => {
|
||||
it("renders", () => {
|
||||
const wrapper = mount(<SavedGardenHUD dispatch={jest.fn()} />);
|
||||
|
|
|
@ -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<HTMLInputElement>) =>
|
||||
this.setState({ searchTerm: e.currentTarget.value });
|
||||
|
||||
render() {
|
||||
return <DesignerPanel panelName={"saved-garden"} panel={Panel.SavedGardens}>
|
||||
<DesignerNavTabs />
|
||||
|
@ -46,8 +44,9 @@ export class RawSavedGardens
|
|||
panel={Panel.SavedGardens}
|
||||
linkTo={"/app/designer/gardens/add"}
|
||||
title={t("Add garden")}>
|
||||
<input type="text" onChange={this.onChange} name="searchTerm"
|
||||
placeholder={t("Search your gardens...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your gardens...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<EmptyStateWrapper
|
||||
notEmpty={this.props.savedGardens.length > 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 }) =>
|
||||
<div className="saved-garden-indicator">
|
||||
|
|
|
@ -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("<Tools />", () => {
|
||||
const fakeProps = (): ToolsProps => ({
|
||||
|
@ -117,8 +118,7 @@ describe("<Tools />", () => {
|
|||
p.tools[0].body.name = "tool 0";
|
||||
p.tools[1].body.name = "tool 1";
|
||||
const wrapper = shallow<Tools>(<Tools {...p} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "0" } });
|
||||
wrapper.find(SearchField).simulate("change", "0");
|
||||
expect(wrapper.state().searchTerm).toEqual("0");
|
||||
});
|
||||
|
||||
|
|
|
@ -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<ToolsProps, ToolsState> {
|
||||
state: ToolsState = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
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<ToolsProps, ToolsState> {
|
|||
panel={Panel.Tools}
|
||||
linkTo={!hasTools ? "/app/designer/tools/add" : undefined}
|
||||
title={!hasTools ? this.strings.titleText : undefined}>
|
||||
<input type="text" onChange={this.update} name="searchTerm"
|
||||
placeholder={this.strings.placeholder} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={this.strings.placeholder}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"tools"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
} from "../weeds_inventory";
|
||||
import { fakeState } from "../../../__test_support__/fake_state";
|
||||
import { fakeWeed } from "../../../__test_support__/fake_state/resources";
|
||||
import { SearchField } from "../../../ui/search_field";
|
||||
|
||||
describe("<Weeds> />", () => {
|
||||
const fakeProps = (): WeedsProps => ({
|
||||
|
@ -20,8 +21,7 @@ describe("<Weeds> />", () => {
|
|||
|
||||
it("changes search term", () => {
|
||||
const wrapper = shallow<Weeds>(<Weeds {...fakeProps()} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "0" } });
|
||||
wrapper.find(SearchField).simulate("change", "0");
|
||||
expect(wrapper.state().searchTerm).toEqual("0");
|
||||
});
|
||||
|
||||
|
|
|
@ -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<WeedsProps, WeedsState> {
|
||||
state: WeedsState = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
this.setState({ searchTerm: currentTarget.value });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <DesignerPanel panelName={"weeds-inventory"} panel={Panel.Weeds}>
|
||||
<DesignerNavTabs />
|
||||
|
@ -44,8 +41,9 @@ export class RawWeeds extends React.Component<WeedsProps, WeedsState> {
|
|||
panel={Panel.Weeds}
|
||||
linkTo={"/app/designer/weeds/add"}
|
||||
title={t("Add weed")}>
|
||||
<input type="text" onChange={this.update} name="searchTerm"
|
||||
placeholder={t("Search your weeds...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your weeds...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"weeds-inventory"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -15,6 +15,7 @@ import { fakePointGroup } from "../../../__test_support__/fake_state/resources";
|
|||
import { history } from "../../../history";
|
||||
import { initSaveGetId } from "../../../api/crud";
|
||||
import { DesignerPanelTop } from "../../designer_panel";
|
||||
import { SearchField } from "../../../ui/search_field";
|
||||
|
||||
describe("<Zones> />", () => {
|
||||
const fakeProps = (): ZonesProps => ({
|
||||
|
@ -30,8 +31,7 @@ describe("<Zones> />", () => {
|
|||
|
||||
it("changes search term", () => {
|
||||
const wrapper = shallow<Zones>(<Zones {...fakeProps()} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "0" } });
|
||||
wrapper.find(SearchField).simulate("change", "0");
|
||||
expect(wrapper.state().searchTerm).toEqual("0");
|
||||
});
|
||||
|
||||
|
|
|
@ -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<ZonesProps, ZonesState> {
|
||||
state: ZonesState = { searchTerm: "" };
|
||||
|
||||
update = ({ currentTarget }: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
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<ZonesProps, ZonesState> {
|
|||
}))
|
||||
.then((id: number) => this.navigate(id)).catch(() => { })}
|
||||
title={t("Add zone")}>
|
||||
<input type="text" onChange={this.update} name="searchTerm"
|
||||
placeholder={t("Search your zones...")} />
|
||||
<SearchField searchTerm={this.state.searchTerm}
|
||||
placeholder={t("Search your zones...")}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</DesignerPanelTop>
|
||||
<DesignerPanelContent panelName={"zones-inventory"}>
|
||||
<EmptyStateWrapper
|
||||
|
|
|
@ -59,6 +59,7 @@ import {
|
|||
} from "../actions";
|
||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { SpecialStatus, Color } from "farmbot";
|
||||
import { SearchField } from "../../ui/search_field";
|
||||
|
||||
const fakeRootFolder = (): FolderNodeInitial => ({
|
||||
kind: "initial",
|
||||
|
@ -540,9 +541,7 @@ describe("<FolderPanelTop />", () => {
|
|||
it("changes search term", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<FolderPanelTop {...p} />);
|
||||
wrapper.find("input").simulate("change", {
|
||||
currentTarget: { value: "new" }
|
||||
});
|
||||
wrapper.find(SearchField).simulate("change", "new");
|
||||
expect(updateSearchTerm).toHaveBeenCalledWith("new");
|
||||
});
|
||||
|
||||
|
|
|
@ -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<FolderProps, FolderState> {
|
|||
|
||||
export const FolderPanelTop = (props: FolderPanelTopProps) =>
|
||||
<div className="panel-top with-button">
|
||||
<div className="thin-search-wrapper">
|
||||
<div className="text-input-wrapper">
|
||||
<i className="fa fa-search" />
|
||||
<input
|
||||
value={props.searchTerm || ""}
|
||||
onChange={e => updateSearchTerm(e.currentTarget.value)}
|
||||
type="text" name="searchTerm"
|
||||
placeholder={t("Search sequences...")} />
|
||||
</div>
|
||||
</div>
|
||||
<SearchField
|
||||
placeholder={t("Search sequences...")}
|
||||
searchTerm={props.searchTerm || ""}
|
||||
onChange={updateSearchTerm} />
|
||||
<ToggleFolderBtn
|
||||
expanded={props.toggleDirection}
|
||||
onClick={props.toggleAll} />
|
||||
|
|
|
@ -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("<Logs />", () => {
|
||||
function fakeLogs(): TaggedLog[] {
|
||||
|
@ -176,8 +177,7 @@ describe("<Logs />", () => {
|
|||
it("changes search term", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow<Logs>(<Logs {...p} />);
|
||||
wrapper.find("input").first().simulate("change",
|
||||
{ currentTarget: { value: "one" } });
|
||||
wrapper.find(SearchField).first().simulate("change", "one");
|
||||
expect(wrapper.state().searchTerm).toEqual("one");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<LogsProps, Partial<LogsState>> {
|
|||
</Row>
|
||||
<Row>
|
||||
<Col xs={12} md={5} lg={4}>
|
||||
<div className="thin-search-wrapper">
|
||||
<div className="text-input-wrapper">
|
||||
<i className="fa fa-search" />
|
||||
<input name="searchTerm"
|
||||
onChange={e =>
|
||||
this.setState({ searchTerm: e.currentTarget.value })}
|
||||
placeholder={t("Search logs...")} />
|
||||
</div>
|
||||
</div>
|
||||
<SearchField
|
||||
placeholder={t("Search logs...")}
|
||||
searchTerm={this.state.searchTerm}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
|
|
|
@ -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("<RegimensList />", () => {
|
||||
function fakeProps(): RegimensListProps {
|
||||
|
@ -25,8 +24,8 @@ describe("<RegimensList />", () => {
|
|||
});
|
||||
|
||||
it("sets search term", () => {
|
||||
const wrapper = mount<RegimensList>(<RegimensList {...fakeProps()} />);
|
||||
wrapper.instance().onChange(inputEvent("term"));
|
||||
const wrapper = shallow<RegimensList>(<RegimensList {...fakeProps()} />);
|
||||
wrapper.find("RegimenListHeader").simulate("change", "term");
|
||||
expect(wrapper.state().searchTerm).toEqual("term");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<HTMLInputElement>): void;
|
||||
searchTerm: string;
|
||||
onChange(searchTerm: string): void;
|
||||
regimenCount: number;
|
||||
dispatch: Function;
|
||||
}
|
||||
|
||||
const RegimenListHeader = (props: RegimenListHeaderProps) =>
|
||||
<div className={"panel-top with-button"}>
|
||||
<div className="thin-search-wrapper">
|
||||
<div className="text-input-wrapper">
|
||||
<i className="fa fa-search" />
|
||||
<input name="searchTerm"
|
||||
onChange={props.onChange}
|
||||
placeholder={t("Search regimens...")} />
|
||||
</div>
|
||||
</div>
|
||||
<SearchField
|
||||
placeholder={t("Search regimens...")}
|
||||
searchTerm={props.searchTerm}
|
||||
onChange={props.onChange} />
|
||||
<AddRegimen dispatch={props.dispatch} length={props.regimenCount} />
|
||||
</div>;
|
||||
|
||||
|
@ -55,16 +53,13 @@ export class RegimensList extends
|
|||
</Col>;
|
||||
}
|
||||
|
||||
onChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
this.setState({ searchTerm: e.currentTarget.value });
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={"regimens-list-wrapper"}>
|
||||
<RegimenListHeader
|
||||
dispatch={this.props.dispatch}
|
||||
regimenCount={this.props.regimens.length}
|
||||
onChange={this.onChange} />
|
||||
searchTerm={this.state.searchTerm}
|
||||
onChange={searchTerm => this.setState({ searchTerm })} />
|
||||
<Row>
|
||||
<EmptyStateWrapper
|
||||
notEmpty={this.props.regimens.length > 0}
|
||||
|
|
|
@ -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("<SearchField />", () => {
|
||||
const fakeProps = (): SearchFieldProps => ({
|
||||
onChange: jest.fn(),
|
||||
searchTerm: "",
|
||||
placeholder: "search...",
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
const wrapper = mount(<SearchField {...fakeProps()} />);
|
||||
expect(wrapper.find("input").props().placeholder).toEqual("search...");
|
||||
});
|
||||
|
||||
it("changes search term", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<SearchField {...p} />);
|
||||
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(<SearchField {...p} />);
|
||||
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(<SearchField {...p} />);
|
||||
const e = changeEvent("new");
|
||||
wrapper.find("input").simulate("KeyPress", e);
|
||||
expect(p.onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -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) =>
|
||||
<div className="thin-search-wrapper">
|
||||
<div className="thin-search">
|
||||
<div className="text-input-wrapper">
|
||||
<ErrorBoundary>
|
||||
{props.customLeftIcon || <i className="fa fa-search" />}
|
||||
<input name="searchTerm"
|
||||
value={props.searchTerm}
|
||||
autoFocus={props.autoFocus}
|
||||
onChange={e => props.onChange(e.currentTarget.value)}
|
||||
onKeyPress={e => props.onKeyPress?.(e.currentTarget.value)}
|
||||
placeholder={props.placeholder} />
|
||||
{props.searchTerm && props.customRightIcon}
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
Loading…
Reference in New Issue