🤞 Remove TightlyCoupledDropDown?
This commit is contained in:
parent
79198130cb
commit
bc630124d9
|
@ -78,8 +78,7 @@ describe("<FarmEventForm/>", () => {
|
|||
let i = instance(p);
|
||||
i.forceUpdate();
|
||||
expect(i.state.localCopyDirty).toBe(false);
|
||||
let e = { value: "wow", executable_type: "Sequence" } as any;
|
||||
i.executableSet(e);
|
||||
i.executableSet({ value: "wow", label: "hey", headingId: "Sequence" });
|
||||
i.forceUpdate();
|
||||
expect(i.state.localCopyDirty).toBe(true);
|
||||
expect(i.state.fe.executable_type).toEqual("Sequence");
|
||||
|
@ -93,7 +92,7 @@ describe("<FarmEventForm/>", () => {
|
|||
let exe = i.executableGet();
|
||||
expect(exe.label).toBe("fake");
|
||||
expect(exe.value).toBe(12);
|
||||
expect(exe.executable_type).toBe("Sequence");
|
||||
expect(exe.headingId).toBe("Sequence");
|
||||
});
|
||||
|
||||
it("sets a subfield of state.fe", () => {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
|
||||
describe("utils", () => {
|
||||
it("warns about missed tasks", () => {
|
||||
pending("Write this next.");
|
||||
// maybeWarnAboutMissedTasks
|
||||
import { executableType } from "../../util";
|
||||
|
||||
describe("executableType", () => {
|
||||
it("handles expected values", () => {
|
||||
expect(executableType("Sequence")).toEqual("Sequence");
|
||||
expect(executableType("Regimen")).toEqual("Regimen");
|
||||
});
|
||||
|
||||
it("throws when given bad data", () => {
|
||||
expect(() => executableType("Nope")).toThrowError();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,8 +11,7 @@ import {
|
|||
} from "../interfaces";
|
||||
import {
|
||||
formatTime,
|
||||
formatDate,
|
||||
TightlyCoupledFarmEventDropDown
|
||||
formatDate
|
||||
} from "./map_state_to_props_add_edit";
|
||||
import {
|
||||
BackArrow,
|
||||
|
@ -35,6 +34,7 @@ import { maybeWarnAboutMissedTasks } from "./util";
|
|||
import { TzWarning } from "./tz_warning";
|
||||
import { FarmEventRepeatForm } from "./farm_event_repeat_form";
|
||||
import { scheduleForFarmEvent } from "./calendar/scheduler";
|
||||
import { executableType } from "../util";
|
||||
|
||||
type FormEvent = React.SyntheticEvent<HTMLInputElement>;
|
||||
export const NEVER: TimeUnit = "never";
|
||||
|
@ -83,7 +83,7 @@ export function recombine(vm: FarmEventViewModel): Partial<TaggedFarmEvent["body
|
|||
|
||||
export interface EditFEProps {
|
||||
deviceTimezone: string | undefined;
|
||||
executableOptions: TightlyCoupledFarmEventDropDown[];
|
||||
executableOptions: DropDownItem[];
|
||||
repeatOptions: DropDownItem[];
|
||||
farmEvent: TaggedFarmEvent;
|
||||
dispatch: Function;
|
||||
|
@ -122,11 +122,11 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
|
|||
}
|
||||
}
|
||||
|
||||
executableSet = (e: TightlyCoupledFarmEventDropDown) => {
|
||||
executableSet = (e: DropDownItem) => {
|
||||
if (e.value) {
|
||||
this.setState(betterMerge(this.state, {
|
||||
fe: {
|
||||
executable_type: e.executable_type,
|
||||
executable_type: executableType(e.headingId),
|
||||
executable_id: (e.value || "").toString()
|
||||
},
|
||||
localCopyDirty: true
|
||||
|
@ -134,13 +134,13 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
|
|||
}
|
||||
}
|
||||
|
||||
executableGet = (): TightlyCoupledFarmEventDropDown => {
|
||||
let executable_type: ExecutableType =
|
||||
executableGet = (): DropDownItem => {
|
||||
let headingId: ExecutableType =
|
||||
(this.executable.kind === "sequences") ? "Sequence" : "Regimen";
|
||||
return {
|
||||
value: this.executable.body.id || 0,
|
||||
label: this.executable.body.name,
|
||||
executable_type
|
||||
headingId
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -22,33 +22,7 @@ import {
|
|||
TaggedSequence,
|
||||
TaggedRegimen
|
||||
} from "../../resources/tagged_resources";
|
||||
// import { DropDownItem } from "../../ui/fb_select";
|
||||
|
||||
/**
|
||||
* TODO: Not a fan of this one, but don't have time to refactor today.
|
||||
* DropDownItem[]s should not know what a Regimen is. - RC Apr '17.
|
||||
*
|
||||
* PROBLEM: Drop down item had an id of '6'. But that `id` could have been for a
|
||||
* "regimen" or a "sequence". There's no way to diferentiate as a user
|
||||
* of <FBSelect/>
|
||||
*
|
||||
* Fast Solution: Tack extra information into DropDownItem. This results in
|
||||
* us needing to do type casts and coupling DropDownItem to
|
||||
* FarmEvent, which is unsafe and won't be totally obvious to
|
||||
* new devs.
|
||||
*
|
||||
* Ideal solution: Add a `parentHeading: string;` field to DropDownItem. It
|
||||
* would tell us which heading the DropDownItem came from. we
|
||||
* could infer the `executable_type` based on the heading it
|
||||
* was under. Then do a `groupBy` inside
|
||||
* of <FBSelect/>
|
||||
*/
|
||||
export interface TightlyCoupledFarmEventDropDown {
|
||||
label: string;
|
||||
executable_type: ExecutableType;
|
||||
value: number;
|
||||
heading?: undefined | boolean;
|
||||
}
|
||||
import { DropDownItem } from "../../ui/fb_select";
|
||||
|
||||
export let formatTime = (input: string) => {
|
||||
let iso = new Date(input).toISOString();
|
||||
|
@ -105,43 +79,37 @@ export function mapStateToPropsAddEdit(props: Everything): AddEditFarmEventProps
|
|||
}
|
||||
};
|
||||
|
||||
let executableOptions: TightlyCoupledFarmEventDropDown[] = [];
|
||||
let executableOptions: DropDownItem[] = [];
|
||||
|
||||
// Rick, we should talk about these. -CV 8/3/2017
|
||||
executableOptions.push({
|
||||
label: t("REGIMENS"),
|
||||
heading: true,
|
||||
value: 0,
|
||||
executable_type: "Regimen"
|
||||
headingId: "Regimen"
|
||||
});
|
||||
|
||||
selectAllRegimens(props.resources.index).map(regimen => {
|
||||
// TODO: Remove executable_type from obj since it's
|
||||
// not declared in the interface.
|
||||
if (regimen.kind === "regimens" && regimen.body.id) {
|
||||
executableOptions.push({
|
||||
label: regimen.body.name,
|
||||
executable_type: "Regimen",
|
||||
headingId: "Regimen",
|
||||
value: regimen.body.id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Rick, we should talk about these and make sure it's OK. -CV 8/3/2017
|
||||
executableOptions.push({
|
||||
label: t("SEQUENCES"),
|
||||
heading: true,
|
||||
value: 0,
|
||||
executable_type: "Sequence"
|
||||
headingId: "Sequence"
|
||||
});
|
||||
|
||||
selectAllSequences(props.resources.index).map(sequence => {
|
||||
// TODO: Remove executable_type from obj since it's
|
||||
// not declared in the interface.
|
||||
if (sequence.kind === "sequences" && sequence.body.id) {
|
||||
executableOptions.push({
|
||||
label: sequence.body.name,
|
||||
executable_type: "Sequence",
|
||||
headingId: "Sequence",
|
||||
value: sequence.body.id
|
||||
});
|
||||
}
|
||||
|
@ -159,28 +127,15 @@ export function mapStateToPropsAddEdit(props: Everything): AddEditFarmEventProps
|
|||
* better. I think it would be better to handle this at the source and keep
|
||||
* the ui logic less involved. -CV 8/3/2017
|
||||
* -------------------------- BEGIN -------------------------------------*/
|
||||
// let newExecutableOptions: DropDownItem[] = [
|
||||
// { label: "Regimen", value: 0, children: [] },
|
||||
// { label: "Sequence", value: 0, children: [] }
|
||||
// ];
|
||||
// executableOptions
|
||||
// .filter(x => !x.heading)
|
||||
// .map(x => {
|
||||
// newExecutableOptions.map(container => {
|
||||
// x.executable_type === container.label && container.children &&
|
||||
// container.children.push(x);
|
||||
// });
|
||||
// });
|
||||
let newExecutableOptions = executableOptions
|
||||
.filter(x => !x.heading)
|
||||
.map(x => {
|
||||
return {
|
||||
label: `${x.executable_type}: ${x.label}`,
|
||||
label: `${x.headingId}: ${x.label}`,
|
||||
value: x.value,
|
||||
executable_type: (_.capitalize(x.executable_type) as ExecutableType)
|
||||
headingId: _.capitalize(x.headingId)
|
||||
};
|
||||
});
|
||||
/* -------------------------- END ---------------------------------------*/
|
||||
|
||||
let regimensById = indexRegimenById(props.resources.index);
|
||||
let sequencesById = indexSequenceById(props.resources.index);
|
||||
|
|
|
@ -9,9 +9,6 @@ import {
|
|||
TaggedPlantPointer,
|
||||
TaggedCrop,
|
||||
} from "../resources/tagged_resources";
|
||||
import {
|
||||
TightlyCoupledFarmEventDropDown
|
||||
} from "./farm_events/map_state_to_props_add_edit";
|
||||
import { PlantPointer } from "../interfaces";
|
||||
import { SlotWithTool } from "../resources/interfaces";
|
||||
import { BotState, BotPosition } from "../devices/interfaces";
|
||||
|
@ -100,7 +97,7 @@ export type TaggedExecutable = TaggedSequence | TaggedRegimen;
|
|||
export type ExecutableQuery = (kind: ExecutableType, id: number) => TaggedExecutable;
|
||||
export interface AddEditFarmEventProps {
|
||||
deviceTimezone: string | undefined;
|
||||
executableOptions: TightlyCoupledFarmEventDropDown[];
|
||||
executableOptions: DropDownItem[];
|
||||
repeatOptions: DropDownItem[];
|
||||
farmEvents: TaggedFarmEvent[];
|
||||
regimensById: CowardlyDictionary<TaggedRegimen>;
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as _ from "lodash";
|
|||
import { OpenFarm, CropSearchResult } from "./openfarm";
|
||||
import { DEFAULT_ICON } from "../open_farm/index";
|
||||
import { HttpPromise } from "../util";
|
||||
import { ExecutableType } from "./interfaces";
|
||||
|
||||
let url = (q: string) => `${OpenFarm.cropUrl}?include=pictures&filter=${q}`;
|
||||
type X = HttpPromise<CropSearchResult>;
|
||||
|
@ -27,3 +28,26 @@ export let OFSearch = (searchTerm: string) =>
|
|||
dispatch({ type: "OF_SEARCH_RESULTS_OK", payload });
|
||||
});
|
||||
};
|
||||
|
||||
function isExecutableType(x?: string): x is ExecutableType {
|
||||
const EXECUTABLES: ExecutableType[] = ["Sequence", "Regimen"];
|
||||
return !!EXECUTABLES.includes(x as ExecutableType);
|
||||
}
|
||||
|
||||
/** USE CASE: You have a `string?` type that you are *certain*
|
||||
* is an `ExecutableType`. But the type checker is
|
||||
* complaining.
|
||||
*
|
||||
* PROBLEM: `as ExecutableType` results in less type safety and
|
||||
* makes bugs harder to pin point in production.
|
||||
*
|
||||
* SOLUTION: Run a user defined type guard (`x is ExecutableType`)
|
||||
* and raise a runtime error with the offending string
|
||||
*/
|
||||
export function executableType(input?: string): ExecutableType {
|
||||
if (isExecutableType(input)) {
|
||||
return input;
|
||||
} else {
|
||||
throw new Error("Assumed string was ExecutableType. Got: " + input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,28 +43,4 @@ export class FBSelect extends React.Component<FBSelectProps, {}> {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// recurse(items: DropDownItem[]): React.ReactElement<any> {
|
||||
// return items.map((item, index) => {
|
||||
// if (item) {
|
||||
// return (
|
||||
// <MenuItem key={index} text={item.executable_type} />
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// console.log(this.props.list);
|
||||
// return (
|
||||
// <div className="filter-search">
|
||||
// <Popover position={Position.BOTTOM_LEFT}>
|
||||
// <button className="fb-button green">Select...</button>
|
||||
// <Menu>
|
||||
// {this.recurse(this.props.list)}
|
||||
// </Menu>
|
||||
// </Popover>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue