(props.value);
return (
-
- // The `as any` is due to something related to https://github.com/emotion-js/emotion/issues/2169
- // We may have to redo the conditional getOptionValue/getOptionLabel
+
{...(props as any)}
value={selectedOption}
onSelect={setSelectedOption}
diff --git a/src/inputs/SelectField.test.tsx b/src/inputs/SelectField.test.tsx
index bf7ab2ee8..0567e891b 100644
--- a/src/inputs/SelectField.test.tsx
+++ b/src/inputs/SelectField.test.tsx
@@ -1,6 +1,6 @@
import { click, input, render } from "@homebound/rtl-utils";
import { useState } from "react";
-import { SelectField, SelectFieldProps, Value } from "src/inputs";
+import { idAndName2, SelectField, SelectFieldProps, Value } from "src/inputs";
const options = [
{ id: "1", name: "One" },
@@ -14,13 +14,7 @@ describe("SelectFieldTest", () => {
it("can set a value", async () => {
// Given a MultiSelectField
const { getByRole } = await render(
- o.name}
- getOptionValue={(o) => o.id}
- />,
+ ,
);
// That initially has "One" selected
const text = getByRole("combobox");
@@ -33,11 +27,13 @@ describe("SelectFieldTest", () => {
expect(onSelect).toHaveBeenCalledWith("3");
});
- function TestSelectField(props: Omit, "onSelect">): JSX.Element {
- const [selected, setSelected] = useState(props.value);
+ function TestSelectField(
+ props: Omit, "onSelect">,
+ ): JSX.Element {
+ const [selected, setSelected] = useState(props.value);
return (
-
- {...props}
+
+ {...(props as any)}
value={selected}
onSelect={(value) => {
onSelect(value);
diff --git a/src/inputs/SelectField.tsx b/src/inputs/SelectField.tsx
index ea2ffa484..a70276cca 100644
--- a/src/inputs/SelectField.tsx
+++ b/src/inputs/SelectField.tsx
@@ -1,18 +1,16 @@
import React, { ReactNode } from "react";
import { Value } from "src/inputs";
import { BeamSelectFieldBaseProps, SelectFieldBase } from "src/inputs/internal/SelectFieldBase";
-import { HasIdAndName, Optional } from "src/types";
+import { HasIdAndName } from "src/types";
-export interface SelectFieldProps extends BeamSelectFieldBaseProps {
- /** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. */
- getOptionMenuLabel?: (opt: O) => string | ReactNode;
- getOptionValue: (opt: O) => V;
- getOptionLabel: (opt: O) => string;
+export type SelectFieldProps = BeamSelectFieldBaseProps & {
/** The current value; it can be `undefined`, even if `V` cannot be. */
value: V | undefined;
- onSelect: (value: V, opt: O) => void;
+ onSelect: (value: V2, opt: O) => void;
options: O[];
-}
+} & (O extends HasIdAndName
+ ? { mapOption?: { label?: (opt: O) => string; value?: (opt: O) => V2; menuLabel?: (opt: O) => ReactNode } }
+ : { mapOption: { label: (opt: O) => string; value: (opt: O) => V2; menuLabel?: (opt: O) => ReactNode } });
/**
* Provides a non-native select/dropdown widget.
@@ -20,35 +18,58 @@ export interface SelectFieldProps extends BeamSelectFieldBas
* The `O` type is a list of options to show, the `V` is the primitive value of a
* given `O` (i.e. it's id) that you want to use as the current/selected value.
*/
-export function SelectField(props: SelectFieldProps): JSX.Element;
-export function SelectField, V extends Value>(
- props: Optional, "getOptionValue" | "getOptionLabel">,
-): JSX.Element;
-export function SelectField(
- props: Optional, "getOptionLabel" | "getOptionValue">,
-): JSX.Element {
- const {
- getOptionValue = (opt: O) => (opt as any).id, // if unset, assume O implements HasId
- getOptionLabel = (opt: O) => (opt as any).name, // if unset, assume O implements HasName
- options,
- onSelect,
- value,
- ...otherProps
- } = props;
+export function SelectField(props: SelectFieldProps): JSX.Element {
+ const { options, onSelect, value, mapOption: maybeMapOption, ...otherProps } = props;
+
+ const mapOption = {
+ value: (o: any) => o.id,
+ label: (o: any) => o.name,
+ menuLabel: (o: any) => o.name,
+ ...maybeMapOption,
+ };
return (
mapOption.label(o)}
+ getOptionMenuLabel={(o) => {
+ const mapped = mapOption.label!(o);
+ return mapped.menuLabel ?? mapped.label;
+ }}
+ getOptionValue={(o) => mapOption.value(o)}
values={value ? [value] : []}
onSelect={(values) => {
if (values.length > 0) {
- const selectedOption = options.find((o) => getOptionValue(o) === values[0]);
- onSelect && selectedOption && onSelect(getOptionValue(selectedOption), selectedOption);
+ const selectedOption = options.find((o) => mapOption.value(o) === values[0]);
+ onSelect && selectedOption && onSelect(mapOption.value(selectedOption), selectedOption);
}
}}
/>
);
}
+
+export const identity = {
+ value(o: { value: V }): V {
+ return o.value;
+ },
+ label(o: { label: string }): string {
+ return o.label;
+ },
+};
+
+export function idAndName() {
+ return {
+ value: (o: { id: V; name: string }): V => o.id,
+ label: (o: { id: V; name: string }): String => o.name,
+ };
+}
+
+export const idAndName2 = {
+ value(o: { id: V; name: string }): V {
+ return o.id;
+ },
+ label(o: { id: V; name: string }): string {
+ return o.name;
+ },
+};