Skip to content

Commit c4f0f2a

Browse files
More PR feedback and code cleanup
1 parent d628cbd commit c4f0f2a

File tree

3 files changed

+61
-11
lines changed

3 files changed

+61
-11
lines changed

packages/connect-react/src/components/ControlSelect.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
isOptionWithLabel,
2222
sanitizeOption,
2323
} from "../utils/type-guards";
24+
import { isLabelValueWrapped } from "../utils/label-value";
2425
import { LoadMoreButton } from "./LoadMoreButton";
2526

2627
// XXX T and ConfigurableProp should be related
@@ -91,14 +92,10 @@ export function ControlSelect<T extends PropOptionValue>({
9192
return rawValue.map((o) =>
9293
selectOptions.find((item) => item.value === o) || sanitizeOption(o as T));
9394
}
94-
} else if (rawValue && typeof rawValue === "object" && "__lv" in (rawValue as Record<string, unknown>)) {
95+
} else if (isLabelValueWrapped(rawValue)) {
9596
// Extract the actual option from __lv wrapper and sanitize to LV
9697
// Handle both single objects and arrays wrapped in __lv
9798
const lvContent = (rawValue as Record<string, unknown>).__lv;
98-
if (!lvContent) {
99-
console.warn("Invalid __lv content:", rawValue);
100-
return null;
101-
}
10299
if (Array.isArray(lvContent)) {
103100
return lvContent.map((item) => sanitizeOption(item as T));
104101
}

packages/connect-react/src/hooks/form-context.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
} from "../types";
3737
import { resolveUserId } from "../utils/resolve-user-id";
3838
import { isConfigurablePropOfType } from "../utils/type-guards";
39+
import { hasLabelValueFormat } from "../utils/label-value";
3940

4041
export type AnyFormFieldContext = Omit<FormFieldContext<ConfigurableProp>, "onChange"> & {
4142
onChange: (value: unknown) => void;
@@ -300,7 +301,6 @@ export const FormContextProvider = <T extends ConfigurableProps>({
300301
component.key,
301302
configurableProps,
302303
configuredProps,
303-
enabledOptionalProps,
304304
]);
305305

306306
// these validations are necessary because they might override PropInput for number case for instance
@@ -450,11 +450,7 @@ export const FormContextProvider = <T extends ConfigurableProps>({
450450
// IMPORTANT: Integer props with remote options (like IDs) can be stored in __lv format
451451
// to preserve the display label. We only delete the value if it's NOT in __lv format
452452
// AND not a number, which indicates invalid/corrupted data.
453-
const isLabelValue = value && typeof value === "object" && "__lv" in value;
454-
const isArrayOfLabelValues = Array.isArray(value) && value.length > 0 &&
455-
value.every((item) => item && typeof item === "object" && "__lv" in item);
456-
457-
if (!(isLabelValue || isArrayOfLabelValues)) {
453+
if (!hasLabelValueFormat(value)) {
458454
delete newConfiguredProps[prop.name as keyof ConfiguredProps<T>];
459455
} else {
460456
newConfiguredProps[prop.name as keyof ConfiguredProps<T>] = value;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Utilities for detecting and handling label-value (__lv) format
3+
* used by Pipedream components to preserve display labels for option values
4+
*/
5+
6+
/**
7+
* Checks if a value is wrapped in the __lv format
8+
* @param value - The value to check
9+
* @returns true if value is an object with __lv property containing valid data
10+
*
11+
* @example
12+
* isLabelValueWrapped({ __lv: { label: "Option 1", value: 123 } }) // true
13+
* isLabelValueWrapped({ __lv: null }) // false
14+
* isLabelValueWrapped({ value: 123 }) // false
15+
*/
16+
export function isLabelValueWrapped(value: unknown): boolean {
17+
if (!value || typeof value !== "object") return false;
18+
if (!("__lv" in value)) return false;
19+
20+
const lvContent = (value as Record<string, unknown>).__lv;
21+
return lvContent != null;
22+
}
23+
24+
/**
25+
* Checks if a value is an array of __lv wrapped objects
26+
* @param value - The value to check
27+
* @returns true if value is an array of valid __lv wrapped objects
28+
*
29+
* @example
30+
* isArrayOfLabelValueWrapped([{ __lv: { label: "A", value: 1 } }]) // true
31+
* isArrayOfLabelValueWrapped([]) // false
32+
* isArrayOfLabelValueWrapped([{ value: 1 }]) // false
33+
*/
34+
export function isArrayOfLabelValueWrapped(value: unknown): boolean {
35+
if (!Array.isArray(value)) return false;
36+
if (value.length === 0) return false;
37+
38+
return value.every((item) =>
39+
item &&
40+
typeof item === "object" &&
41+
"__lv" in item &&
42+
(item as Record<string, unknown>).__lv != null);
43+
}
44+
45+
/**
46+
* Checks if a value has the label-value format (either single or array)
47+
* @param value - The value to check
48+
* @returns true if value is in __lv format (single or array)
49+
*
50+
* @example
51+
* hasLabelValueFormat({ __lv: { label: "A", value: 1 } }) // true
52+
* hasLabelValueFormat([{ __lv: { label: "A", value: 1 } }]) // true
53+
* hasLabelValueFormat({ value: 1 }) // false
54+
*/
55+
export function hasLabelValueFormat(value: unknown): boolean {
56+
return isLabelValueWrapped(value) || isArrayOfLabelValueWrapped(value);
57+
}

0 commit comments

Comments
 (0)