Skip to content

Commit

Permalink
minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielburnworth committed Apr 20, 2020
1 parent 0bd6d9a commit 6f484ab
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 112 deletions.
1 change: 1 addition & 0 deletions frontend/__test_support__/mock_fbtoaster.ts
Expand Up @@ -7,4 +7,5 @@ jest.mock("../toast/toast", () => ({
error: jest.fn(),
warning: jest.fn(),
busy: jest.fn(),
removeToast: jest.fn(),
}));
22 changes: 15 additions & 7 deletions frontend/connectivity/__tests__/connect_device/index_test.ts
Expand Up @@ -37,7 +37,9 @@ import { getDevice } from "../../../device";
import { talk } from "browser-speech";
import { MessageType } from "../../../sequences/interfaces";
import { FbjsEventName } from "farmbot/dist/constants";
import { info, error, success, warning, fun, busy } from "../../../toast/toast";
import {
info, error, success, warning, fun, busy, removeToast,
} from "../../../toast/toast";
import { onLogs } from "../../log_handlers";
import { fakeState } from "../../../__test_support__/fake_state";
import { globalQueue } from "../../batch_queue";
Expand Down Expand Up @@ -177,7 +179,8 @@ describe("onOffline", () => {
jest.resetAllMocks();
onOffline();
expect(dispatchNetworkDown).toHaveBeenCalledWith("user.mqtt", ANY_NUMBER);
expect(error).toHaveBeenCalledWith(Content.MQTT_DISCONNECTED);
expect(error).toHaveBeenCalledWith(
Content.MQTT_DISCONNECTED, "Error", "red", "offline");
});
});

Expand All @@ -186,13 +189,17 @@ describe("onOnline", () => {
jest.resetAllMocks();
onOnline();
expect(dispatchNetworkUp).toHaveBeenCalledWith("user.mqtt", ANY_NUMBER);
expect(removeToast).toHaveBeenCalledWith("offline");
});
});

describe("onReconnect", () => {
onReconnect();
expect(warning).toHaveBeenCalledWith(
"Attempting to reconnect to the message broker", "Offline", "yellow");
describe("onReconnect()", () => {
it("sends reconnect toast", () => {
onReconnect();
expect(warning).toHaveBeenCalledWith(
"Attempting to reconnect to the message broker",
"Offline", "yellow", "offline");
});
});

describe("changeLastClientConnected", () => {
Expand Down Expand Up @@ -268,7 +275,8 @@ describe("onPublicBroadcast", () => {
console.log = jest.fn();
onPublicBroadcast({});
expectBroadcastLog();
expect(window.alert).toHaveBeenCalledWith(Content.FORCE_REFRESH_CANCEL_WARNING);
expect(window.alert).toHaveBeenCalledWith(
Content.FORCE_REFRESH_CANCEL_WARNING);
expect(location.assign).not.toHaveBeenCalled();
});
});
31 changes: 17 additions & 14 deletions frontend/connectivity/connect_device.ts
Expand Up @@ -4,7 +4,9 @@ import { Log } from "farmbot/dist/resources/api_resources";
import { Farmbot, BotStateTree, TaggedResource } from "farmbot";
import { FbjsEventName } from "farmbot/dist/constants";
import { noop } from "lodash";
import { success, error, info, warning, fun, busy } from "../toast/toast";
import {
success, error, info, warning, fun, busy, removeToast,
} from "../toast/toast";
import { HardwareState } from "../devices/interfaces";
import { GetState, ReduxAction } from "../redux/interfaces";
import { Content, Actions } from "../constants";
Expand Down Expand Up @@ -102,11 +104,6 @@ export function readStatus() {
.then(() => { commandOK(noun); }, commandErr(noun));
}

export const onOffline = () => {
dispatchNetworkDown("user.mqtt", now());
error(t(Content.MQTT_DISCONNECTED));
};

export const changeLastClientConnected = (bot: Farmbot) => () => {
bot.setUserEnv({
"LAST_CLIENT_CONNECTED": JSON.stringify(new Date())
Expand Down Expand Up @@ -157,14 +154,20 @@ export function onMalformed() {
}
}

export const onOnline =
() => {
success(t("Reconnected to the message broker."), t("Online"));
dispatchNetworkUp("user.mqtt", now());
};
export const onReconnect =
() => warning(t("Attempting to reconnect to the message broker"),
t("Offline"), "yellow");
export const onOnline = () => {
removeToast("offline");
success(t("Reconnected to the message broker."), t("Online"));
dispatchNetworkUp("user.mqtt", now());
};

export const onReconnect = () =>
warning(t("Attempting to reconnect to the message broker"),
t("Offline"), "yellow", "offline");

export const onOffline = () => {
dispatchNetworkDown("user.mqtt", now());
error(t(Content.MQTT_DISCONNECTED), t("Error"), "red", "offline");
};

export function onPublicBroadcast(payl: unknown) {
console.log(FbjsEventName.publicBroadcast, payl);
Expand Down
36 changes: 33 additions & 3 deletions frontend/css/farm_designer/farm_designer_panels.scss
Expand Up @@ -291,10 +291,19 @@
.panel-action-buttons {
position: absolute;
z-index: 9;
height: 25rem;
height: 16rem;
width: 100%;
background: $panel_medium_light_gray;
padding: 0.5rem;
&.status {
height: 20rem;
}
&.more {
height: 23rem;
}
&.more.status {
height: 26rem;
}
button {
margin: 0.5rem;
float: left;
Expand All @@ -303,11 +312,12 @@
min-width: -webkit-fill-available;
margin-bottom: 0px;
margin-left: .5rem;
margin-top: 1rem;
margin-top: 0;
}
.button-row {
float: left;
width: 100%;
margin-bottom: 1rem;
}
.filter-search {
padding-right: 1rem;
Expand All @@ -324,15 +334,35 @@
line-height: 4.1rem;
}
}
.more {
float: right;
cursor: pointer;
margin-right: 1rem;
line-height: 2.5rem;
p {
display: inline;
font-size: 1.4rem;
margin-right: 1rem;
}
}
}
.panel-content {
padding-top: 25rem;
padding-top: 16rem;
padding-right: 0;
padding-left: 0;
padding-bottom: 5rem;
max-height: calc(100vh - 13rem);
overflow-y: auto;
overflow-x: hidden;
&.status {
padding-top: 20rem;
}
&.more {
padding-top: 23rem;
}
&.more.status {
padding-top: 26rem;
}
}
}

Expand Down
38 changes: 37 additions & 1 deletion frontend/farm_designer/plants/__tests__/select_plants_test.tsx
Expand Up @@ -215,6 +215,18 @@ describe("<SelectPlants />", () => {
{ payload: undefined, type: Actions.SELECT_POINT });
});

it("toggles more", () => {
const p = fakeProps();
const wrapper = mount<SelectPlants>(<SelectPlants {...p} />);
expect(wrapper.state().more).toEqual(false);
expect(wrapper.find(".select-more").props().hidden).toBeTruthy();
expect(wrapper.html()).not.toContain(" more status");
wrapper.find(".more").simulate("click");
expect(wrapper.state().more).toEqual(true);
expect(wrapper.find(".select-more").props().hidden).toBeFalsy();
expect(wrapper.html()).toContain(" more status");
});

it("selects group items", () => {
const p = fakeProps();
p.selected = undefined;
Expand All @@ -236,7 +248,7 @@ describe("<SelectPlants />", () => {
expect(wrapper.state().group_id).toEqual(1);
expect(dispatch).toHaveBeenCalledWith({
type: Actions.SET_SELECTION_POINT_TYPE,
payload: POINTER_TYPES,
payload: ["Plant"],
});
expect(p.dispatch).toHaveBeenLastCalledWith({
type: Actions.SELECT_POINT,
Expand Down Expand Up @@ -265,6 +277,30 @@ describe("<SelectPlants />", () => {
});
});

it("selects selection type without criteria", () => {
const p = fakeProps();
const group = fakePointGroup();
group.body.id = 1;
group.body.criteria.string_eq = {};
const plant = fakePlant();
plant.body.id = 1;
const weed = fakeWeed();
weed.body.id = 2;
group.body.point_ids = [1, 2];
p.groups = [group];
const dispatch = jest.fn();
p.dispatch = mockDispatch(dispatch);
const wrapper = mount<SelectPlants>(<SelectPlants {...p} />);
const actionsWrapper = shallow(wrapper.instance().ActionButtons());
actionsWrapper.find("FBSelect").at(1).simulate("change", {
label: "", value: 1
});
expect(dispatch).toHaveBeenCalledWith({
type: Actions.SET_SELECTION_POINT_TYPE,
payload: POINTER_TYPES,
});
});

const DELETE_BTN_INDEX = 4;

it("confirms deletion of selected plants", () => {
Expand Down
62 changes: 45 additions & 17 deletions frontend/farm_designer/plants/select_plants.tsx
Expand Up @@ -25,7 +25,8 @@ import {
} from "farmbot";
import { UUID } from "../../resources/interfaces";
import {
selectAllActivePoints, selectAllToolSlotPointers, selectAllTools, selectAllPointGroups,
selectAllActivePoints, selectAllToolSlotPointers, selectAllTools,
selectAllPointGroups,
} from "../../resources/selectors";
import { PointInventoryItem } from "../points/point_inventory_item";
import { ToolSlotInventoryItem } from "../tools";
Expand Down Expand Up @@ -108,11 +109,12 @@ export interface SelectPlantsProps {

interface SelectPlantsState {
group_id: number | undefined;
more: boolean;
}

export class RawSelectPlants
extends React.Component<SelectPlantsProps, SelectPlantsState> {
state: SelectPlantsState = { group_id: undefined };
state: SelectPlantsState = { group_id: undefined, more: false };

componentDidMount() {
const { dispatch, selected } = this.props;
Expand Down Expand Up @@ -163,16 +165,24 @@ export class RawSelectPlants
this.setState({ group_id });
const group = this.props.groups
.filter(pg => pg.body.id == group_id)[0];
const pointUuids = pointsSelectedByGroup(group, this.props.allPoints)
.map(p => p.uuid);
const points = pointsSelectedByGroup(group, this.props.allPoints);
const pointUuids = points.map(p => p.uuid);
const pointerTypes =
group.body.criteria.string_eq.pointer_type as PointType[] | undefined;
this.props.dispatch(setSelectionPointType(pointerTypes || POINTER_TYPES));
const uniqPointTypes = uniq(points.map(p => p.body.pointer_type));
const pointTypes =
uniqPointTypes.length == 1 ? [uniqPointTypes[0]] : undefined;
this.props.dispatch(setSelectionPointType(
pointerTypes || pointTypes || POINTER_TYPES));
this.props.dispatch(selectPoint(pointUuids));
}

ActionButtons = () =>
<div className="panel-action-buttons">
<div className={["panel-action-buttons",
this.state.more ? "more" : "",
["Plant", "Weed"].includes(this.selectionPointType) ? "status" : "",
].join(" ")}>
<label>{t("selection type")}</label>
<FBSelect key={this.selectionPointType}
list={POINTER_TYPE_LIST()}
selectedItem={POINTER_TYPE_DDI_LOOKUP()[this.selectionPointType]}
Expand All @@ -185,22 +195,36 @@ export class RawSelectPlants
<div className="button-row">
<button className="fb-button gray"
title={t("Select none")}
onClick={() => this.props.dispatch(selectPoint(undefined))}>
onClick={() => {
this.setState({ group_id: undefined });
this.props.dispatch(selectPoint(undefined));
}}>
{t("Select none")}
</button>
<button className="fb-button gray"
title={t("Select all")}
onClick={() => this.props.dispatch(selectPoint(this.allPointUuids))}>
onClick={() => {
this.setState({ group_id: undefined });
this.props.dispatch(selectPoint(this.allPointUuids));
}}>
{t("Select all")}
</button>
<label>{t("select all in group")}</label>
<FBSelect key={this.selectionPointType}
list={Object.values(this.groupDDILookup)}
selectedItem={this.state.group_id
? this.groupDDILookup[this.state.group_id]
: undefined}
customNullLabel={t("Select a group")}
onChange={this.selectGroup} />
<div className="more"
onClick={() => this.setState({ more: !this.state.more })}>
<p>{this.state.more ? t("Less") : t("More")}</p>
<i className={`fa fa-caret-${this.state.more ? "up" : "down"}`}
title={this.state.more ? t("less") : t("more")} />
</div>
<div className={"select-more"} hidden={!this.state.more}>
<label>{t("select all in group")}</label>
<FBSelect key={`${this.selectionPointType}-${this.state.group_id}`}
list={Object.values(this.groupDDILookup)}
selectedItem={this.state.group_id
? this.groupDDILookup[this.state.group_id]
: undefined}
customNullLabel={t("Select a group")}
onChange={this.selectGroup} />
</div>
</div>
<label>{t("SELECTION ACTIONS")}</label>
<div className="button-row">
Expand Down Expand Up @@ -273,7 +297,11 @@ export class RawSelectPlants
description={Content.BOX_SELECT_DESCRIPTION} />
<this.ActionButtons />

<DesignerPanelContent panelName={"plant-selection"}>
<DesignerPanelContent panelName={"plant-selection"}
className={[
this.state.more ? "more" : "",
["Plant", "Weed"].includes(this.selectionPointType) ? "status" : "",
].join(" ")}>
{this.selectedPointData.map(p => {
if (p.kind == "PlantTemplate" || p.body.pointer_type == "Plant") {
return <PlantInventoryItem
Expand Down
30 changes: 28 additions & 2 deletions frontend/toast/__tests__/fb_toast_test.ts
Expand Up @@ -2,9 +2,11 @@ import { FBToast } from "../fb_toast";

describe("FBToast", () => {
let count = 0;
const newToast = (): [FBToast, HTMLDivElement] => {
const newToast = (idPrefix = ""): [FBToast, HTMLDivElement] => {
const parent = document.createElement("div");
const child = new FBToast(parent, "title", "message" + (count++), "red");
const child =
new FBToast(parent, "title", "message" + (count++), "red", idPrefix);
parent.appendChild(child.toastEl);
return [child, parent];
};

Expand Down Expand Up @@ -90,6 +92,30 @@ describe("FBToast", () => {
i.detach();
expect(FBToast.everyMessage[message]).toBeFalsy();
expect(p.removeChild).toHaveBeenCalledWith(i.toastEl);
expect(i.isAttached).toBeFalsy();
});

it("doesn't detach from the DOM", () => {
const [i, p] = newToast();
p.innerHTML = "";
const { message } = i;
FBToast.everyMessage[message] = true;
p.removeChild = jest.fn();
i.isAttached = true;
i.detach();
expect(FBToast.everyMessage[message]).toBeFalsy();
expect(p.removeChild).not.toHaveBeenCalled();
expect(i.isAttached).toBeTruthy();
});

it("sets id", () => {
const toast = newToast("id-prefix")[0];
expect(toast.toastEl.id).toEqual(expect.stringMatching("^id-prefix-toast-"));
});

it("doesn't set id", () => {
const toast = newToast()[0];
expect(toast.toastEl.id).toEqual("");
});

it("does polling", () => {
Expand Down

0 comments on commit 6f484ab

Please sign in to comment.