refactor using new ts features
parent
a32d9f025b
commit
a38e7b6b91
|
@ -7,11 +7,11 @@ export function clickButton(
|
||||||
position: number,
|
position: number,
|
||||||
text: string,
|
text: string,
|
||||||
options?: { partial_match?: boolean, button_tag?: string }) {
|
options?: { partial_match?: boolean, button_tag?: string }) {
|
||||||
const btnTag = options && options.button_tag ? options.button_tag : "button";
|
const btnTag = options?.button_tag ? options.button_tag : "button";
|
||||||
const button = wrapper.find(btnTag).at(position);
|
const button = wrapper.find(btnTag).at(position);
|
||||||
const expectedText = text.toLowerCase();
|
const expectedText = text.toLowerCase();
|
||||||
const actualText = button.text().toLowerCase();
|
const actualText = button.text().toLowerCase();
|
||||||
options && options.partial_match
|
options?.partial_match
|
||||||
? expect(actualText).toContain(expectedText)
|
? expect(actualText).toContain(expectedText)
|
||||||
: expect(actualText).toEqual(expectedText);
|
: expect(actualText).toEqual(expectedText);
|
||||||
button.simulate("click");
|
button.simulate("click");
|
||||||
|
|
|
@ -32,7 +32,7 @@ test("buildResourceIndex - add a FarmEvent", () => {
|
||||||
const key = Object.keys(db.index.byKind.FarmEvent)[0];
|
const key = Object.keys(db.index.byKind.FarmEvent)[0];
|
||||||
const fe = db.index.references[key];
|
const fe = db.index.references[key];
|
||||||
expect(fe).toBeTruthy();
|
expect(fe).toBeTruthy();
|
||||||
if (fe && fe.kind === "FarmEvent") {
|
if (fe?.kind === "FarmEvent") {
|
||||||
const { body } = fe;
|
const { body } = fe;
|
||||||
expect(body).toEqual(STUB_RESOURCE.body);
|
expect(body).toEqual(STUB_RESOURCE.body);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { AxisInputBoxProps } from "./interfaces";
|
import { AxisInputBoxProps } from "./interfaces";
|
||||||
import { Col, BlurableInput } from "../ui/index";
|
import { Col, BlurableInput } from "../ui/index";
|
||||||
import { isUndefined } from "lodash";
|
|
||||||
|
|
||||||
export const AxisInputBox = ({ onChange, value, axis }: AxisInputBoxProps) => {
|
export const AxisInputBox = ({ onChange, value, axis }: AxisInputBoxProps) => {
|
||||||
return <Col xs={3}>
|
return <Col xs={3}>
|
||||||
<BlurableInput
|
<BlurableInput
|
||||||
value={(isUndefined(value) ? "" : value)}
|
value={value ?? ""}
|
||||||
type="number"
|
type="number"
|
||||||
allowEmpty={true}
|
allowEmpty={true}
|
||||||
onCommit={e => {
|
onCommit={e => {
|
||||||
|
|
|
@ -36,8 +36,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
grayscale={grayscale && grayscale.x}
|
grayscale={grayscale?.x}
|
||||||
disabled={disable && disable.x}
|
disabled={disable?.x}
|
||||||
dim={!xParam.consistent}
|
dim={!xParam.consistent}
|
||||||
toggleValue={xParam.value}
|
toggleValue={xParam.value}
|
||||||
toggleAction={() =>
|
toggleAction={() =>
|
||||||
|
@ -45,8 +45,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
grayscale={grayscale && grayscale.y}
|
grayscale={grayscale?.y}
|
||||||
disabled={disable && disable.y}
|
disabled={disable?.y}
|
||||||
dim={!yParam.consistent}
|
dim={!yParam.consistent}
|
||||||
toggleValue={yParam.value}
|
toggleValue={yParam.value}
|
||||||
toggleAction={() =>
|
toggleAction={() =>
|
||||||
|
@ -54,8 +54,8 @@ export function BooleanMCUInputGroup(props: BooleanMCUInputGroupProps) {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={2} className={"centered-button-div"}>
|
<Col xs={2} className={"centered-button-div"}>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
grayscale={grayscale && grayscale.z}
|
grayscale={grayscale?.z}
|
||||||
disabled={disable && disable.z}
|
disabled={disable?.z}
|
||||||
dim={!zParam.consistent}
|
dim={!zParam.consistent}
|
||||||
toggleValue={zParam.value}
|
toggleValue={zParam.value}
|
||||||
toggleAction={() =>
|
toggleAction={() =>
|
||||||
|
|
|
@ -115,7 +115,7 @@ describe("<PinBindingInputGroup/>", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const key = Object.keys(p.resources.byKind.Sequence)[0];
|
const key = Object.keys(p.resources.byKind.Sequence)[0];
|
||||||
const s = p.resources.references[key];
|
const s = p.resources.references[key];
|
||||||
const id = s && s.body.id;
|
const id = s?.body.id;
|
||||||
const wrapper = mount<PinBindingInputGroup>(<PinBindingInputGroup {...p} />);
|
const wrapper = mount<PinBindingInputGroup>(<PinBindingInputGroup {...p} />);
|
||||||
expect(wrapper.instance().state.sequenceIdInput).toEqual(undefined);
|
expect(wrapper.instance().state.sequenceIdInput).toEqual(undefined);
|
||||||
wrapper.instance().setSequenceIdInput({ label: "label", value: "" + id });
|
wrapper.instance().setSequenceIdInput({ label: "label", value: "" + id });
|
||||||
|
|
|
@ -79,8 +79,8 @@ export const mcuParamValidator =
|
||||||
(ok: () => void, no?: (message: string) => void): void => {
|
(ok: () => void, no?: (message: string) => void): void => {
|
||||||
const validator = edgeCases[key];
|
const validator = edgeCases[key];
|
||||||
const result = validator && validator(key, val, state);
|
const result = validator && validator(key, val, state);
|
||||||
if (result && result.outcome === "NO") {
|
if (result?.outcome === "NO") {
|
||||||
return (no && no(result.errorMessage));
|
return (no?.(result.errorMessage));
|
||||||
} else {
|
} else {
|
||||||
return ok();
|
return ok();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe("draggableReducer", () => {
|
||||||
const dt = nextState.dataTransfer;
|
const dt = nextState.dataTransfer;
|
||||||
expect(Object.keys(dt)).toContain(payload.uuid);
|
expect(Object.keys(dt)).toContain(payload.uuid);
|
||||||
const entry = dt[payload.uuid];
|
const entry = dt[payload.uuid];
|
||||||
expect(entry && entry.uuid).toEqual(payload.uuid);
|
expect(entry?.uuid).toEqual(payload.uuid);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("drops a step", () => {
|
it("drops a step", () => {
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const DesignerPanelHeader = (props: DesignerPanelHeaderProps) => {
|
||||||
title={t("go back") + backToText(props.backTo)}
|
title={t("go back") + backToText(props.backTo)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.backTo ? routeHistory.push(props.backTo) : history.back();
|
props.backTo ? routeHistory.push(props.backTo) : history.back();
|
||||||
props.onBack && props.onBack();
|
props.onBack?.();
|
||||||
}} />
|
}} />
|
||||||
{props.title &&
|
{props.title &&
|
||||||
<span className={`title ${textColor}-text`}>
|
<span className={`title ${textColor}-text`}>
|
||||||
|
|
|
@ -16,10 +16,10 @@ export function occurrence(
|
||||||
CalendarOccurrence {
|
CalendarOccurrence {
|
||||||
const normalHeading = fe.executable.name || fe.executable_type;
|
const normalHeading = fe.executable.name || fe.executable_type;
|
||||||
const heading = () => {
|
const heading = () => {
|
||||||
if (modifiers && modifiers.empty) {
|
if (modifiers?.empty) {
|
||||||
return "*Empty*";
|
return "*Empty*";
|
||||||
}
|
}
|
||||||
if (modifiers && modifiers.numHidden) {
|
if (modifiers?.numHidden) {
|
||||||
return `+ ${modifiers.numHidden} more: ` + normalHeading;
|
return `+ ${modifiers.numHidden} more: ` + normalHeading;
|
||||||
}
|
}
|
||||||
return normalHeading;
|
return normalHeading;
|
||||||
|
|
|
@ -36,7 +36,7 @@ const addOrRemoveFromGroup =
|
||||||
const group = fetchGroupFromUrl(resources);
|
const group = fetchGroupFromUrl(resources);
|
||||||
const point =
|
const point =
|
||||||
resources.references[clickedPlantUuid] as TaggedPoint | undefined;
|
resources.references[clickedPlantUuid] as TaggedPoint | undefined;
|
||||||
if (group && point && point.body.id) {
|
if (group && point?.body.id) {
|
||||||
type Body = (typeof group)["body"];
|
type Body = (typeof group)["body"];
|
||||||
const nextGroup: Body = ({
|
const nextGroup: Body = ({
|
||||||
...group.body,
|
...group.body,
|
||||||
|
@ -54,7 +54,7 @@ const addOrRemoveFromSelection =
|
||||||
(clickedPlantUuid: UUID, selectedPlants: UUID[] | undefined) => {
|
(clickedPlantUuid: UUID, selectedPlants: UUID[] | undefined) => {
|
||||||
const nextSelected =
|
const nextSelected =
|
||||||
(selectedPlants || []).filter(uuid => uuid !== clickedPlantUuid);
|
(selectedPlants || []).filter(uuid => uuid !== clickedPlantUuid);
|
||||||
if (!(selectedPlants && selectedPlants.includes(clickedPlantUuid))) {
|
if (!(selectedPlants?.includes(clickedPlantUuid))) {
|
||||||
nextSelected.push(clickedPlantUuid);
|
nextSelected.push(clickedPlantUuid);
|
||||||
}
|
}
|
||||||
return selectPlant(nextSelected);
|
return selectPlant(nextSelected);
|
||||||
|
|
|
@ -21,7 +21,7 @@ function getNewTrailArray(update: TrailRecord, watering: boolean): TrailRecord[]
|
||||||
const arr: TrailRecord[] = JSON.parse(get(sessionStorage, key, "[]"));
|
const arr: TrailRecord[] = JSON.parse(get(sessionStorage, key, "[]"));
|
||||||
if (arr.length > (trailLength - 1)) { arr.shift(); } // max length reached
|
if (arr.length > (trailLength - 1)) { arr.shift(); } // max length reached
|
||||||
const last = arr[arr.length - 1]; // most recent item in array
|
const last = arr[arr.length - 1]; // most recent item in array
|
||||||
if (update && update.coord &&
|
if (update?.coord &&
|
||||||
(!last || !isEqual(last.coord, update.coord))) { // coordinate comparison
|
(!last || !isEqual(last.coord, update.coord))) { // coordinate comparison
|
||||||
arr.push(update); // unique addition
|
arr.push(update); // unique addition
|
||||||
} else { // nothing new to add, increase water circle size if watering
|
} else { // nothing new to add, increase water circle size if watering
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { TaggedPlant } from "../map/interfaces";
|
||||||
import { DesignerPanel, DesignerPanelHeader } from "../designer_panel";
|
import { DesignerPanel, DesignerPanelHeader } from "../designer_panel";
|
||||||
import { t } from "../../i18next_wrapper";
|
import { t } from "../../i18next_wrapper";
|
||||||
import { EditPlantInfoProps, PlantOptions } from "../interfaces";
|
import { EditPlantInfoProps, PlantOptions } from "../interfaces";
|
||||||
import { isString, isUndefined } from "lodash";
|
import { isString } from "lodash";
|
||||||
import { history, getPathArray } from "../../history";
|
import { history, getPathArray } from "../../history";
|
||||||
import { destroy, edit, save } from "../../api/crud";
|
import { destroy, edit, save } from "../../api/crud";
|
||||||
import { BooleanSetting } from "../../session_keys";
|
import { BooleanSetting } from "../../session_keys";
|
||||||
|
@ -20,7 +20,7 @@ export class RawPlantInfo extends React.Component<EditPlantInfoProps, {}> {
|
||||||
get confirmDelete() {
|
get confirmDelete() {
|
||||||
const confirmSetting = this.props.getConfigValue(
|
const confirmSetting = this.props.getConfigValue(
|
||||||
BooleanSetting.confirm_plant_deletion);
|
BooleanSetting.confirm_plant_deletion);
|
||||||
return isUndefined(confirmSetting) ? true : confirmSetting;
|
return confirmSetting ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy = (plantUUID: string) => {
|
destroy = (plantUUID: string) => {
|
||||||
|
|
|
@ -51,7 +51,7 @@ describe("", () => {
|
||||||
|
|
||||||
const sort = (sortType: PointGroupSortType): string[] => {
|
const sort = (sortType: PointGroupSortType): string[] => {
|
||||||
const array = SORT_OPTIONS[sortType](plants as TaggedPlant[]);
|
const array = SORT_OPTIONS[sortType](plants as TaggedPlant[]);
|
||||||
return array.map(x => x && x.body && (x.body.name || "NA"));
|
return array.map(x => x?.body?.name || "NA");
|
||||||
};
|
};
|
||||||
|
|
||||||
it("sorts randomly", () => {
|
it("sorts randomly", () => {
|
||||||
|
|
|
@ -66,7 +66,7 @@ export const mapStateToProps = (props: Everything): EditGardenProps => {
|
||||||
const savedGarden = findSavedGardenByUrl(props.resources.index);
|
const savedGarden = findSavedGardenByUrl(props.resources.index);
|
||||||
return {
|
return {
|
||||||
savedGarden,
|
savedGarden,
|
||||||
gardenIsOpen: !!(savedGarden && savedGarden.uuid === openedSavedGarden),
|
gardenIsOpen: !!(savedGarden?.uuid === openedSavedGarden),
|
||||||
dispatch: props.dispatch,
|
dispatch: props.dispatch,
|
||||||
plantPointerCount: selectAllPlantPointers(props.resources.index).length,
|
plantPointerCount: selectAllPlantPointers(props.resources.index).length,
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,7 +71,7 @@ const Setting = (props: SettingProps) => {
|
||||||
toggleValue={props.invert ? !value : value}
|
toggleValue={props.invert ? !value : value}
|
||||||
toggleAction={() => {
|
toggleAction={() => {
|
||||||
props.dispatch(setWebAppConfigValue(setting, !value));
|
props.dispatch(setWebAppConfigValue(setting, !value));
|
||||||
callback && callback();
|
callback?.();
|
||||||
}}
|
}}
|
||||||
title={`${t("toggle")} ${title}`}
|
title={`${t("toggle")} ${title}`}
|
||||||
customText={{ textFalse: t("off"), textTrue: t("on") }} />}
|
customText={{ textFalse: t("off"), textTrue: t("on") }} />}
|
||||||
|
|
|
@ -100,7 +100,7 @@ export class RawAddToolSlot
|
||||||
: "initializing"}
|
: "initializing"}
|
||||||
<SaveBtn onClick={this.save} status={SpecialStatus.DIRTY} />
|
<SaveBtn onClick={this.save} status={SpecialStatus.DIRTY} />
|
||||||
</DesignerPanelContent>
|
</DesignerPanelContent>
|
||||||
</DesignerPanel >;
|
</DesignerPanel>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function FarmwareConfigMenu(props: FarmwareConfigMenuProps) {
|
||||||
className="fb-button gray fa fa-download"
|
className="fb-button gray fa fa-download"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const p = getDevice().installFirstPartyFarmware();
|
const p = getDevice().installFirstPartyFarmware();
|
||||||
p && p.catch(commandErr("Farmware installation"));
|
p?.catch(commandErr("Farmware installation"));
|
||||||
}}
|
}}
|
||||||
disabled={props.firstPartyFwsInstalled} />
|
disabled={props.firstPartyFwsInstalled} />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -108,6 +108,6 @@ export function FarmwareForm(props: FarmwareFormProps): JSX.Element {
|
||||||
|
|
||||||
/** Determine if a Farmware has requested inputs. */
|
/** Determine if a Farmware has requested inputs. */
|
||||||
export function needsFarmwareForm(farmware: FarmwareManifestInfo): Boolean {
|
export function needsFarmwareForm(farmware: FarmwareManifestInfo): Boolean {
|
||||||
const needsWidget = farmware.config && farmware.config.length > 0;
|
const needsWidget = farmware.config?.length > 0;
|
||||||
return needsWidget;
|
return needsWidget;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ const PendingInstallNameError =
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const installation: TaggedFarmwareInstallation | undefined =
|
const installation: TaggedFarmwareInstallation | undefined =
|
||||||
installations.filter(x => x.body.url === url)[0];
|
installations.filter(x => x.body.url === url)[0];
|
||||||
const packageError = installation && installation.body.package_error;
|
const packageError = installation?.body.package_error;
|
||||||
return (url && installation && packageError)
|
return (url && installation && packageError)
|
||||||
? <div className="error-with-button">
|
? <div className="error-with-button">
|
||||||
<label>{t("Could not fetch package name")}</label>
|
<label>{t("Could not fetch package name")}</label>
|
||||||
|
|
|
@ -116,7 +116,7 @@ export class Photos extends React.Component<PhotosProps, {}> {
|
||||||
|
|
||||||
deletePhoto = () => {
|
deletePhoto = () => {
|
||||||
const img = this.props.currentImage || this.props.images[0];
|
const img = this.props.currentImage || this.props.images[0];
|
||||||
if (img && img.uuid) {
|
if (img?.uuid) {
|
||||||
this.props.dispatch(destroy(img.uuid))
|
this.props.dispatch(destroy(img.uuid))
|
||||||
.then(() => success(t("Image Deleted.")))
|
.then(() => success(t("Image Deleted.")))
|
||||||
.catch(() => error(t("Could not delete image.")));
|
.catch(() => error(t("Could not delete image.")));
|
||||||
|
|
|
@ -61,7 +61,7 @@ export class ImageWorkspace extends React.Component<ImageWorkspaceProps, {}> {
|
||||||
|
|
||||||
maybeProcessPhoto = () => {
|
maybeProcessPhoto = () => {
|
||||||
const img = this.props.currentImage || this.props.images[0];
|
const img = this.props.currentImage || this.props.images[0];
|
||||||
if (img && img.body.id) {
|
if (img?.body.id) {
|
||||||
this.props.onProcessPhoto(img.body.id);
|
this.props.onProcessPhoto(img.body.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const FolderListItem = (props: FolderItemProps) => {
|
||||||
onMouseUp={() => props.toggleSequenceMove(sequence.uuid)} />
|
onMouseUp={() => props.toggleSequenceMove(sequence.uuid)} />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</StepDragger >;
|
</StepDragger>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ToggleFolderBtn = (props: ToggleFolderBtnProps) => {
|
const ToggleFolderBtn = (props: ToggleFolderBtnProps) => {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { TaggedResource, TaggedSequence } from "farmbot";
|
import { TaggedResource, TaggedSequence } from "farmbot";
|
||||||
|
|
||||||
import { RootFolderNode, FolderUnion } from "./constants";
|
import { RootFolderNode, FolderUnion } from "./constants";
|
||||||
|
|
||||||
export interface FolderSearchProps {
|
export interface FolderSearchProps {
|
||||||
|
|
|
@ -18,7 +18,7 @@ export const clickHandler =
|
||||||
/** BEGIN LEGACY SHIMS */
|
/** BEGIN LEGACY SHIMS */
|
||||||
const { onClick, to } = props;
|
const { onClick, to } = props;
|
||||||
navigate(maybeStripLegacyUrl(to));
|
navigate(maybeStripLegacyUrl(to));
|
||||||
onClick && onClick(e);
|
onClick?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Link extends React.Component<LinkProps, {}> {
|
export class Link extends React.Component<LinkProps, {}> {
|
||||||
|
|
|
@ -180,7 +180,7 @@ const FirmwareChoiceTable = () =>
|
||||||
export const changeFirmwareHardware = (dispatch: Function | undefined) =>
|
export const changeFirmwareHardware = (dispatch: Function | undefined) =>
|
||||||
(ddi: DropDownItem) => {
|
(ddi: DropDownItem) => {
|
||||||
if (isFwHardwareValue(ddi.value)) {
|
if (isFwHardwareValue(ddi.value)) {
|
||||||
dispatch && dispatch(updateConfig({ firmware_hardware: ddi.value }));
|
dispatch?.(updateConfig({ firmware_hardware: ddi.value }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const computeEditorUrlFromState =
|
||||||
: resources.consumers.regimens.currentRegimen;
|
: resources.consumers.regimens.currentRegimen;
|
||||||
const r = resources.index.references[current || ""];
|
const r = resources.index.references[current || ""];
|
||||||
const base = `/app/${resource === "Sequence" ? "sequences" : "regimens"}/`;
|
const base = `/app/${resource === "Sequence" ? "sequences" : "regimens"}/`;
|
||||||
if (r && r.kind == resource) {
|
if (r?.kind == resource) {
|
||||||
return base + urlFriendly(r.body.name);
|
return base + urlFriendly(r.body.name);
|
||||||
} else {
|
} else {
|
||||||
return base;
|
return base;
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
|
||||||
BooleanSetting.disable_emergency_unlock_confirmation)} />
|
BooleanSetting.disable_emergency_unlock_confirmation)} />
|
||||||
|
|
||||||
AccountMenu = () => {
|
AccountMenu = () => {
|
||||||
const hasName = this.props.user && this.props.user.body.name;
|
const hasName = this.props.user?.body.name;
|
||||||
const firstName = hasName ?
|
const firstName = hasName ?
|
||||||
`${hasName.split(" ")[0].slice(0, 9)} ▾` : `${t("Menu")} ▾`;
|
`${hasName.split(" ")[0].slice(0, 9)} ▾` : `${t("Menu")} ▾`;
|
||||||
return <div className="menu-popover">
|
return <div className="menu-popover">
|
||||||
|
|
|
@ -43,10 +43,7 @@ const promiseCache: Dictionary<Promise<Readonly<OFCropAttrs>>> = {};
|
||||||
|
|
||||||
const cacheTheIcon = (slug: string) =>
|
const cacheTheIcon = (slug: string) =>
|
||||||
(resp: AxiosResponse<OFCropResponse>): OFIcon => {
|
(resp: AxiosResponse<OFCropResponse>): OFIcon => {
|
||||||
if (resp
|
if (resp?.data?.data?.attributes) {
|
||||||
&& resp.data
|
|
||||||
&& resp.data.data
|
|
||||||
&& resp.data.data.attributes) {
|
|
||||||
const icon = {
|
const icon = {
|
||||||
slug: resp.data.data.attributes.slug,
|
slug: resp.data.data.attributes.slug,
|
||||||
spread: resp.data.data.attributes.spread,
|
spread: resp.data.data.attributes.spread,
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function getMiddleware(env: EnvName) {
|
||||||
.map((mwc) => mwc.fn);
|
.map((mwc) => mwc.fn);
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
const wow = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
const wow = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
||||||
const dtCompose = wow && wow({
|
const dtCompose = wow?.({
|
||||||
actionsBlacklist: [
|
actionsBlacklist: [
|
||||||
Actions.NETWORK_EDGE_CHANGE,
|
Actions.NETWORK_EDGE_CHANGE,
|
||||||
Actions.PING_NO,
|
Actions.PING_NO,
|
||||||
|
|
|
@ -12,10 +12,8 @@ const WEB_APP_CONFIG: ResourceName = "WebAppConfig";
|
||||||
* resources, downloading the filtered log list as required from the API. */
|
* resources, downloading the filtered log list as required from the API. */
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
export const fn: Middleware = () => (dispatch) => (action: any) => {
|
export const fn: Middleware = () => (dispatch) => (action: any) => {
|
||||||
const needsRefresh = action
|
const needsRefresh = action?.payload?.kind === WEB_APP_CONFIG
|
||||||
&& action.payload
|
&& action.type === Actions.SAVE_RESOURCE_OK;
|
||||||
&& action.type === Actions.SAVE_RESOURCE_OK
|
|
||||||
&& action.payload.kind === WEB_APP_CONFIG;
|
|
||||||
|
|
||||||
needsRefresh && throttledLogRefresh(dispatch);
|
needsRefresh && throttledLogRefresh(dispatch);
|
||||||
|
|
||||||
|
|
|
@ -23,16 +23,11 @@ const WEB_APP_CONFIG: ResourceName = "WebAppConfig";
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
const fn: Middleware = () => (dispatch) => (action: any) => {
|
const fn: Middleware = () => (dispatch) => (action: any) => {
|
||||||
const x: DeepPartial<SyncResponse<TaggedWebAppConfig>> = action;
|
const x: DeepPartial<SyncResponse<TaggedWebAppConfig>> = action;
|
||||||
if (x
|
if (x?.type === Actions.RESOURCE_READY
|
||||||
&& x.type === Actions.RESOURCE_READY
|
&& x.payload?.body
|
||||||
&& x.payload
|
|
||||||
&& x.payload.body
|
|
||||||
&& x.payload.kind === WEB_APP_CONFIG) {
|
&& x.payload.kind === WEB_APP_CONFIG) {
|
||||||
const conf = arrayUnwrap(x.payload.body);
|
const conf = arrayUnwrap(x.payload.body);
|
||||||
conf
|
conf?.body?.disable_i18n && revertToEnglish();
|
||||||
&& conf.body
|
|
||||||
&& conf.body.disable_i18n
|
|
||||||
&& revertToEnglish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dispatch(action);
|
return dispatch(action);
|
||||||
|
|
|
@ -12,10 +12,9 @@ export function dontStopThem() { }
|
||||||
const shouldStop =
|
const shouldStop =
|
||||||
(allResources: TaggedResource[], config: TaggedWebAppConfig | undefined) => {
|
(allResources: TaggedResource[], config: TaggedWebAppConfig | undefined) => {
|
||||||
const loggedIn = !!localStorage.getItem("session");
|
const loggedIn = !!localStorage.getItem("session");
|
||||||
const discardUnsaved = config && config.body.discard_unsaved;
|
const discardUnsaved = config?.body.discard_unsaved;
|
||||||
const sequenceResources = allResources.filter(r => r.kind === "Sequence");
|
const sequenceResources = allResources.filter(r => r.kind === "Sequence");
|
||||||
const discardUnsavedSequences =
|
const discardUnsavedSequences = config?.body.discard_unsaved_sequences;
|
||||||
config && config.body.discard_unsaved_sequences;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For the unsaved notification to show, a user must:
|
* For the unsaved notification to show, a user must:
|
||||||
|
@ -59,6 +58,6 @@ export function registerSubscribers(store: Store) {
|
||||||
subscriptions.forEach(function (s) {
|
subscriptions.forEach(function (s) {
|
||||||
ENV_LIST.includes &&
|
ENV_LIST.includes &&
|
||||||
ENV_LIST.includes(s.env) &&
|
ENV_LIST.includes(s.env) &&
|
||||||
store.subscribe(() => s.fn && s.fn(store.getState()));
|
store.subscribe(() => s.fn?.(store.getState()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ const fn: MW =
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
(action: any) => {
|
(action: any) => {
|
||||||
const fbos = getVersionFromState(store.getState());
|
const fbos = getVersionFromState(store.getState());
|
||||||
window.Rollbar && window.Rollbar.configure({ payload: { fbos } });
|
window.Rollbar?.configure({ payload: { fbos } });
|
||||||
return dispatch(action);
|
return dispatch(action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ const BAD_UUID = "WARNING: Not a sequence UUID.";
|
||||||
export class BulkScheduler extends React.Component<BulkEditorProps, {}> {
|
export class BulkScheduler extends React.Component<BulkEditorProps, {}> {
|
||||||
selected = (): DropDownItem => {
|
selected = (): DropDownItem => {
|
||||||
const s = this.props.selectedSequence;
|
const s = this.props.selectedSequence;
|
||||||
return (s && s.body.id)
|
return (s?.body.id)
|
||||||
? { label: s.body.name, value: s.uuid }
|
? { label: s.body.name, value: s.uuid }
|
||||||
: NULL_CHOICE;
|
: NULL_CHOICE;
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,7 @@ export class BulkScheduler extends React.Component<BulkEditorProps, {}> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dispatch, weeks, sequences } = this.props;
|
const { dispatch, weeks, sequences } = this.props;
|
||||||
const active = !!(sequences && sequences.length);
|
const active = !!(sequences?.length);
|
||||||
return <div className="bulk-scheduler-content">
|
return <div className="bulk-scheduler-content">
|
||||||
<AddButton
|
<AddButton
|
||||||
active={active}
|
active={active}
|
||||||
|
|
|
@ -66,7 +66,7 @@ describe("<RegimenEditor />", () => {
|
||||||
p.dispatch = jest.fn(() => Promise.resolve());
|
p.dispatch = jest.fn(() => Promise.resolve());
|
||||||
const wrapper = mount(<RegimenEditor {...p} />);
|
const wrapper = mount(<RegimenEditor {...p} />);
|
||||||
clickButton(wrapper, 2, "delete");
|
clickButton(wrapper, 2, "delete");
|
||||||
const expectedUuid = p.current && p.current.uuid;
|
const expectedUuid = p.current?.uuid;
|
||||||
expect(destroy).toHaveBeenCalledWith(expectedUuid);
|
expect(destroy).toHaveBeenCalledWith(expectedUuid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ describe("<RegimenEditor />", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
const wrapper = mount(<RegimenEditor {...p} />);
|
const wrapper = mount(<RegimenEditor {...p} />);
|
||||||
clickButton(wrapper, 0, "save", { partial_match: true });
|
clickButton(wrapper, 0, "save", { partial_match: true });
|
||||||
const expectedUuid = p.current && p.current.uuid;
|
const expectedUuid = p.current?.uuid;
|
||||||
expect(save).toHaveBeenCalledWith(expectedUuid);
|
expect(save).toHaveBeenCalledWith(expectedUuid);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -177,7 +177,7 @@ describe("maybeGetSequence", () => {
|
||||||
const i = buildResourceIndex([s]);
|
const i = buildResourceIndex([s]);
|
||||||
const result = Selector.maybeGetSequence(i.index, s.uuid);
|
const result = Selector.maybeGetSequence(i.index, s.uuid);
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
result && expect(result.uuid).toBe(s.uuid);
|
expect(result?.uuid).toBe(s.uuid);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ const reindexAllSequences = (i: ResourceIndex) => {
|
||||||
const mapper = reindexSequences(i);
|
const mapper = reindexSequences(i);
|
||||||
betterCompact(Object.keys(i.byKind["Sequence"]).map(uuid => {
|
betterCompact(Object.keys(i.byKind["Sequence"]).map(uuid => {
|
||||||
const resource = i.references[uuid];
|
const resource = i.references[uuid];
|
||||||
return (resource && resource.kind == "Sequence") ? resource : undefined;
|
return (resource?.kind == "Sequence") ? resource : undefined;
|
||||||
})).map(mapper);
|
})).map(mapper);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ const AFTER_HOOKS: IndexerHook = {
|
||||||
FbosConfig: (i) => {
|
FbosConfig: (i) => {
|
||||||
const conf = getFbosConfig(i);
|
const conf = getFbosConfig(i);
|
||||||
|
|
||||||
if (conf && conf.body.boot_sequence_id) {
|
if (conf?.body.boot_sequence_id) {
|
||||||
const { boot_sequence_id } = conf.body;
|
const { boot_sequence_id } = conf.body;
|
||||||
const tracker = i.inUse["Sequence.FbosConfig"];
|
const tracker = i.inUse["Sequence.FbosConfig"];
|
||||||
const uuid = i.byKindAndId[joinKindAndId("Sequence", boot_sequence_id)];
|
const uuid = i.byKindAndId[joinKindAndId("Sequence", boot_sequence_id)];
|
||||||
|
@ -345,7 +345,7 @@ export const indexUpsert: IndexUpsert = (db, resources, strategy) => {
|
||||||
const { kind } = arrayUnwrap(resources);
|
const { kind } = arrayUnwrap(resources);
|
||||||
// Clean up indexes (if needed)
|
// Clean up indexes (if needed)
|
||||||
const before = BEFORE_HOOKS[kind];
|
const before = BEFORE_HOOKS[kind];
|
||||||
before && before(db, strategy);
|
before?.(db, strategy);
|
||||||
|
|
||||||
// Run indexers
|
// Run indexers
|
||||||
ups.map(callback => {
|
ups.map(callback => {
|
||||||
|
@ -354,14 +354,14 @@ export const indexUpsert: IndexUpsert = (db, resources, strategy) => {
|
||||||
|
|
||||||
// Finalize indexing (if needed)
|
// Finalize indexing (if needed)
|
||||||
const after = AFTER_HOOKS[kind];
|
const after = AFTER_HOOKS[kind];
|
||||||
after && after(db, strategy);
|
after?.(db, strategy);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function indexRemove(db: ResourceIndex, resource: TaggedResource) {
|
export function indexRemove(db: ResourceIndex, resource: TaggedResource) {
|
||||||
downs.map(callback => arrayWrap(resource).map(r => callback(r, db)));
|
downs.map(callback => arrayWrap(resource).map(r => callback(r, db)));
|
||||||
// Finalize indexing (if needed)
|
// Finalize indexing (if needed)
|
||||||
const after = AFTER_HOOKS[resource.kind];
|
const after = AFTER_HOOKS[resource.kind];
|
||||||
after && after(db, "ongoing");
|
after?.(db, "ongoing");
|
||||||
}
|
}
|
||||||
|
|
||||||
export const beforeEach = (state: RestResources,
|
export const beforeEach = (state: RestResources,
|
||||||
|
|
|
@ -77,7 +77,7 @@ export function findPointerByTypeAndId(index: ResourceIndex,
|
||||||
const uuid = "" + index.byKindAndId[pni];
|
const uuid = "" + index.byKindAndId[pni];
|
||||||
const resource = index.references[uuid];
|
const resource = index.references[uuid];
|
||||||
|
|
||||||
if (resource && resource.kind === "Point") {
|
if (resource?.kind === "Point") {
|
||||||
return resource;
|
return resource;
|
||||||
} else {
|
} else {
|
||||||
// We might have a sequence dependency leak if this exception is ever
|
// We might have a sequence dependency leak if this exception is ever
|
||||||
|
@ -204,7 +204,7 @@ export function maybeGetTimeSettings(index: ResourceIndex): TimeSettings {
|
||||||
|
|
||||||
export function maybeGetDevice(index: ResourceIndex): TaggedDevice | undefined {
|
export function maybeGetDevice(index: ResourceIndex): TaggedDevice | undefined {
|
||||||
const dev = index.references[Object.keys(index.byKind.Device)[0] || "nope"];
|
const dev = index.references[Object.keys(index.byKind.Device)[0] || "nope"];
|
||||||
return (dev && dev.kind === "Device") ?
|
return (dev?.kind === "Device") ?
|
||||||
dev : undefined;
|
dev : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ export function maybeFetchUser(index: ResourceIndex):
|
||||||
if (user && sanityCheck(user) && list.length > 1) {
|
if (user && sanityCheck(user) && list.length > 1) {
|
||||||
throw new Error("PROBLEM: Expected 1 user. Got: " + list.length);
|
throw new Error("PROBLEM: Expected 1 user. Got: " + list.length);
|
||||||
}
|
}
|
||||||
if ((list.length === 1) && user && user.kind === "User") {
|
if ((list.length === 1) && user?.kind === "User") {
|
||||||
return user;
|
return user;
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const determineVector =
|
||||||
return ts ? ts.body : undefined;
|
return ts ? ts.body : undefined;
|
||||||
case "identifier":
|
case "identifier":
|
||||||
const variable = maybeFindVariable(node.args.label, resources, uuid);
|
const variable = maybeFindVariable(node.args.label, resources, uuid);
|
||||||
return variable && variable.vector;
|
return variable?.vector;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -127,7 +127,7 @@ describe("<SequenceEditorMiddleActive/>", () => {
|
||||||
p.dispatch = dispatch;
|
p.dispatch = dispatch;
|
||||||
const wrapper = mount(<SequenceEditorMiddleActive {...p} />);
|
const wrapper = mount(<SequenceEditorMiddleActive {...p} />);
|
||||||
const props = wrapper.find("DropArea").props() as DropAreaProps;
|
const props = wrapper.find("DropArea").props() as DropAreaProps;
|
||||||
props.callback && props.callback("key");
|
props.callback?.("key");
|
||||||
dispatch.mock.calls[0][0](() =>
|
dispatch.mock.calls[0][0](() =>
|
||||||
({ value: 1, intent: "step_splice", draggerId: 2 }));
|
({ value: 1, intent: "step_splice", draggerId: 2 }));
|
||||||
expect(splice).toHaveBeenCalledWith(expect.objectContaining({
|
expect(splice).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
|
|
@ -148,7 +148,7 @@ const SequenceBtnGroup = ({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const confirm = getWebAppConfigValue(
|
const confirm = getWebAppConfigValue(
|
||||||
BooleanSetting.confirm_sequence_deletion);
|
BooleanSetting.confirm_sequence_deletion);
|
||||||
const force = isUndefined(confirm) ? false : !confirm;
|
const force = !(confirm ?? true);
|
||||||
dispatch(destroy(sequence.uuid, force))
|
dispatch(destroy(sequence.uuid, force))
|
||||||
.then(() => push("/app/sequences/"));
|
.then(() => push("/app/sequences/"));
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
export function mapStateToProps(props: Everything): Props {
|
export function mapStateToProps(props: Everything): Props {
|
||||||
const uuid = props.resources.consumers.sequences.current;
|
const uuid = props.resources.consumers.sequences.current;
|
||||||
const sequence = uuid ? findSequence(props.resources.index, uuid) : undefined;
|
const sequence = uuid ? findSequence(props.resources.index, uuid) : undefined;
|
||||||
sequence && (sequence.body.body || []).map(x => getStepTag(x));
|
(sequence?.body.body || []).map(x => getStepTag(x));
|
||||||
|
|
||||||
const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index));
|
const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index));
|
||||||
const { mcu_params } = props.bot.hardware;
|
const { mcu_params } = props.bot.hardware;
|
||||||
|
|
|
@ -83,7 +83,7 @@ describe("<TileExecuteScript/>", () => {
|
||||||
it("shows special 1st-party Farmware name", () => {
|
it("shows special 1st-party Farmware name", () => {
|
||||||
const p = fakeProps();
|
const p = fakeProps();
|
||||||
(p.currentStep as ExecuteScript).args.label = "plant-detection";
|
(p.currentStep as ExecuteScript).args.label = "plant-detection";
|
||||||
p.farmwareData && p.farmwareData.farmwareNames.push("plant-detection");
|
p.farmwareData?.farmwareNames.push("plant-detection");
|
||||||
const wrapper = mount(<TileExecuteScript {...p} />);
|
const wrapper = mount(<TileExecuteScript {...p} />);
|
||||||
expect(wrapper.find("label").length).toEqual(1);
|
expect(wrapper.find("label").length).toEqual(1);
|
||||||
expect(wrapper.text()).toContain("Weed Detector");
|
expect(wrapper.text()).toContain("Weed Detector");
|
||||||
|
|
|
@ -54,7 +54,7 @@ export function TileExecuteScript(props: StepParams) {
|
||||||
|
|
||||||
/** Configs (inputs) from Farmware manifest for <FarmwareInputs />. */
|
/** Configs (inputs) from Farmware manifest for <FarmwareInputs />. */
|
||||||
const currentFarmwareConfigDefaults = (fwName: string): FarmwareConfig[] => {
|
const currentFarmwareConfigDefaults = (fwName: string): FarmwareConfig[] => {
|
||||||
return farmwareData && farmwareData.farmwareConfigs[fwName]
|
return farmwareData?.farmwareConfigs[fwName]
|
||||||
? farmwareData.farmwareConfigs[fwName]
|
? farmwareData.farmwareConfigs[fwName]
|
||||||
: [];
|
: [];
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,8 @@ import {
|
||||||
const fakeResourceIndex = buildResourceIndex(FAKE_RESOURCES).index;
|
const fakeResourceIndex = buildResourceIndex(FAKE_RESOURCES).index;
|
||||||
const fakeTaggedSequence = fakeResourceIndex
|
const fakeTaggedSequence = fakeResourceIndex
|
||||||
.references[Object.keys(fakeResourceIndex.byKind.Sequence)[0]] as TaggedSequence;
|
.references[Object.keys(fakeResourceIndex.byKind.Sequence)[0]] as TaggedSequence;
|
||||||
const fakeId = fakeTaggedSequence && fakeTaggedSequence.body.id || 0;
|
const fakeId = fakeTaggedSequence.body.id || 0;
|
||||||
const fakeName = fakeTaggedSequence && fakeTaggedSequence.body.name || "";
|
const fakeName = fakeTaggedSequence.body.name || "";
|
||||||
const expectedItem = { label: fakeName, value: fakeId };
|
const expectedItem = { label: fakeName, value: fakeId };
|
||||||
|
|
||||||
function fakeProps(): IfParams {
|
function fakeProps(): IfParams {
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const newTaggedResource = <T extends TR>(kind: T["kind"],
|
||||||
return {
|
return {
|
||||||
kind: kind as TaggedResource["kind"],
|
kind: kind as TaggedResource["kind"],
|
||||||
body: body as TaggedResource["body"],
|
body: body as TaggedResource["body"],
|
||||||
uuid: generateUuid(body && body.id ? body.id : undefined, kind),
|
uuid: generateUuid(body?.id, kind),
|
||||||
specialStatus
|
specialStatus
|
||||||
} as T;
|
} as T;
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class ToolBayList extends React.Component<ToolBayListProps, {}> {
|
||||||
ToolSlotListItem = (slot: TaggedToolSlotPointer, index: number) => {
|
ToolSlotListItem = (slot: TaggedToolSlotPointer, index: number) => {
|
||||||
const { getToolByToolSlotUUID } = this.props;
|
const { getToolByToolSlotUUID } = this.props;
|
||||||
const tool = getToolByToolSlotUUID(slot.uuid);
|
const tool = getToolByToolSlotUUID(slot.uuid);
|
||||||
const name = (tool && tool.body.name) || t("None");
|
const name = (tool?.body.name) || t("None");
|
||||||
return <Row key={slot.uuid}>
|
return <Row key={slot.uuid}>
|
||||||
<Col xs={1}><label>{index + 1}</label></Col>
|
<Col xs={1}><label>{index + 1}</label></Col>
|
||||||
<Col xs={2}>{slot.body.gantry_mounted ? t("Gantry") : slot.body.x}</Col>
|
<Col xs={2}>{slot.body.gantry_mounted ? t("Gantry") : slot.body.x}</Col>
|
||||||
|
|
|
@ -6,7 +6,7 @@ interface BackArrowProps {
|
||||||
export function BackArrow(props: BackArrowProps) {
|
export function BackArrow(props: BackArrowProps) {
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
history.back();
|
history.back();
|
||||||
props.onClick && props.onClick();
|
props.onClick?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
return <a onClick={onClick} className="back-arrow">
|
return <a onClick={onClick} className="back-arrow">
|
||||||
|
|
|
@ -47,7 +47,7 @@ export class BlurableInput extends React.Component<BIProps, Partial<BIState>> {
|
||||||
withinLimits = (options?: { toasts?: boolean }): boolean => {
|
withinLimits = (options?: { toasts?: boolean }): boolean => {
|
||||||
const onError = (msg: string) => {
|
const onError = (msg: string) => {
|
||||||
this.setState({ error: msg });
|
this.setState({ error: msg });
|
||||||
options && options.toasts && error(msg);
|
options?.toasts && error(msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.props.type === "number") {
|
if (this.props.type === "number") {
|
||||||
|
|
|
@ -131,18 +131,6 @@ describe("util", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isUndefined()", () => {
|
|
||||||
it("undefined", () => {
|
|
||||||
const result = Util.isUndefined(undefined);
|
|
||||||
expect(result).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("defined", () => {
|
|
||||||
const result = Util.isUndefined({});
|
|
||||||
expect(result).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("randomColor()", () => {
|
describe("randomColor()", () => {
|
||||||
it("only picks valid colors", () => {
|
it("only picks valid colors", () => {
|
||||||
times(Util.colors.length * 1.5, () =>
|
times(Util.colors.length * 1.5, () =>
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function prettyPrintApiErrors(err: AxiosErrorResponse) {
|
||||||
|
|
||||||
function safelyFetchErrors(err: AxiosErrorResponse): Dictionary<string> {
|
function safelyFetchErrors(err: AxiosErrorResponse): Dictionary<string> {
|
||||||
// In case the interpreter gives us an oddball error message.
|
// In case the interpreter gives us an oddball error message.
|
||||||
if (err && err.response && err.response.data) {
|
if (err.response?.data) {
|
||||||
return err.response.data;
|
return err.response.data;
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
@ -42,7 +42,7 @@ export function bail(message: string): never {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const catchErrors = (error: Error) => {
|
export const catchErrors = (error: Error) => {
|
||||||
if (window.Rollbar && window.Rollbar.error) {
|
if (window.Rollbar?.error) {
|
||||||
window.Rollbar.error(error);
|
window.Rollbar.error(error);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import {
|
||||||
sortBy,
|
sortBy,
|
||||||
merge,
|
merge,
|
||||||
isNumber,
|
isNumber,
|
||||||
isUndefined as lodashIsUndefined
|
|
||||||
} from "lodash";
|
} from "lodash";
|
||||||
import { t } from "../i18next_wrapper";
|
import { t } from "../i18next_wrapper";
|
||||||
|
|
||||||
|
@ -88,10 +87,6 @@ export type CowardlyDictionary<T> = Dictionary<T | undefined>;
|
||||||
*/
|
*/
|
||||||
export const NOT_SAVED = -1;
|
export const NOT_SAVED = -1;
|
||||||
|
|
||||||
export function isUndefined(x: object | undefined): x is undefined {
|
|
||||||
return lodashIsUndefined(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Better than Array.proto.filter and compact() because the type checker
|
/** Better than Array.proto.filter and compact() because the type checker
|
||||||
* knows what's going on.
|
* knows what's going on.
|
||||||
*/
|
*/
|
||||||
|
@ -190,7 +185,7 @@ export function validBotLocationData(
|
||||||
*/
|
*/
|
||||||
export function validFwConfig(config: TaggedFirmwareConfig | undefined):
|
export function validFwConfig(config: TaggedFirmwareConfig | undefined):
|
||||||
TaggedFirmwareConfig["body"] | undefined {
|
TaggedFirmwareConfig["body"] | undefined {
|
||||||
return (config && config.body.api_migrated)
|
return (config?.body.api_migrated)
|
||||||
? config.body
|
? config.body
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
@ -200,7 +195,7 @@ export function validFwConfig(config: TaggedFirmwareConfig | undefined):
|
||||||
*/
|
*/
|
||||||
export function validFbosConfig(
|
export function validFbosConfig(
|
||||||
config: TaggedFbosConfig | undefined): TaggedFbosConfig["body"] | undefined {
|
config: TaggedFbosConfig | undefined): TaggedFbosConfig["body"] | undefined {
|
||||||
return (config && config.body.api_migrated)
|
return (config?.body.api_migrated)
|
||||||
? config.body
|
? config.body
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
@ -232,4 +227,4 @@ export const parseIntInput = (input: string): number => {
|
||||||
|
|
||||||
export const timeFormatString =
|
export const timeFormatString =
|
||||||
(timeSettings: TimeSettings | undefined): string =>
|
(timeSettings: TimeSettings | undefined): string =>
|
||||||
(timeSettings && timeSettings.hour24) ? "H:mm" : "h:mma";
|
(timeSettings?.hour24) ? "H:mm" : "h:mma";
|
||||||
|
|
Loading…
Reference in New Issue