Skip to content
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 5 additions & 11 deletions packages/visual-editor/src/editor/yextEntityFieldUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe("getFieldsForSelector", () => {
);
});

it("allows string descendants to satisfy rich text item source requirements", () => {
it("allows item sources when any descendant type matches", () => {
const fields = getFieldsForSelector(
{
fields: [
Expand Down Expand Up @@ -82,7 +82,7 @@ describe("getFieldsForSelector", () => {
},
},
{
itemSourceTypes: [["type.rich_text_v2"]],
itemSourceTypes: [["type.image"], ["type.rich_text_v2"]],
}
);

Expand All @@ -95,7 +95,7 @@ describe("getFieldsForSelector", () => {
);
});

it("applies rich text compatibility to mapped source descendant checks", () => {
it("hides item sources when no descendant types match", () => {
const fields = getFieldsForSelector(
{
fields: [
Expand Down Expand Up @@ -127,17 +127,11 @@ describe("getFieldsForSelector", () => {
},
},
{
mappedSourceTypes: [["type.rich_text_v2"]],
itemSourceTypes: [["type.image"], ["type.cta"]],
}
);

expect(fields).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "c_articles",
}),
])
);
expect(fields).toEqual([]);
});

it("merges duplicate scoped fields when one has a display name and another has nested children", () => {
Expand Down
68 changes: 9 additions & 59 deletions packages/visual-editor/src/editor/yextEntityFieldUtils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
type EntityFieldTypes,
getFilteredEntityFields,
getCompatibleEntityFieldTypes,
RenderEntityFieldFilter,
} from "../internal/utils/getFilteredEntityFields.ts";
import { StreamFields, YextSchemaField } from "../types/entityFields.ts";
import { resolveField } from "../utils/resolveYextEntityField.ts";
import { type StreamDocument } from "../utils/types/StreamDocument.ts";
import { getTopLevelLinkedEntitySourceFields } from "../utils/linkedEntityFieldUtils.ts";
import {
getListSourceRootFields,
type MappedSourceFieldFilter,
Expand Down Expand Up @@ -205,16 +205,6 @@ export const getEntityFieldScopeDisplayName = (
return displayName?.split(DISPLAY_NAME_SEPARATOR).at(-1);
};

/**
* Returns whether a resolved mapped-source root is compatible with linked-item
* source selection: undefined, null, arrays, and object values are all valid.
*/
const isMappedListSourceValue = (value: unknown): boolean =>
value === undefined ||
value === null ||
Array.isArray(value) ||
(!!value && typeof value === "object");

const hasListSourceValueInDocument = (
streamDocument: StreamDocument,
fieldName: string
Expand Down Expand Up @@ -315,9 +305,8 @@ const getScopedFieldsForSelector = (
* Returns the schema fields that should appear in an entity field selector.
*
* 1. Scope to a selected source item when `sourceField` is provided.
* 2. For item-source and mapped-source pickers, restrict roots to fields that
* can satisfy the required descendant type sets. `itemSourceTypes` takes
* precedence over `mappedSourceTypes` when both are present.
* 2. For item-source pickers, restrict roots to fields whose descendants can
* satisfy at least one configured type group.
* 3. Filter incompatible resolved values out when a stream document is
* available.
* 4. Fall back to normal entity-field filtering for standard field selectors.
Expand All @@ -332,8 +321,7 @@ export const getFieldsForSelector = (
return getScopedFieldsForSelector(entityFields, sourceField, filter);
}

const requiredDescendantTypes =
filter.itemSourceTypes ?? filter.mappedSourceTypes;
const requiredDescendantTypes = filter.itemSourceTypes;

const hasRequiredDescendants = (field: YextSchemaField): boolean => {
if (!requiredDescendantTypes?.length) {
Expand All @@ -347,7 +335,8 @@ export const getFieldsForSelector = (
}
);

return requiredDescendantTypes.every((requiredTypes) =>
// One descendant match is enough for item-source selection.
const matchesRequiredTypes = (requiredTypes: EntityFieldTypes[]): boolean =>
availableFields.some(
(availableField) =>
getFilteredEntityFields(
Expand All @@ -357,8 +346,9 @@ export const getFieldsForSelector = (
types: requiredTypes.flatMap(getCompatibleEntityFieldTypes),
}
).length > 0
)
);
);

return requiredDescendantTypes.some(matchesRequiredTypes);
};

if (filter.itemSourceTypes?.length) {
Expand All @@ -382,46 +372,6 @@ export const getFieldsForSelector = (
);
}

if (filter.mappedSourceTypes?.length) {
const validLinkedEntityRootFields = getTopLevelLinkedEntitySourceFields(
entityFields
)
.map((field) => ({
...field,
displayName:
getEntityFieldDisplayName(field.name, entityFields) ??
field.displayName ??
field.name,
}))
.filter(hasRequiredDescendants)
.filter((field) =>
!streamDocument
? true
: isMappedListSourceValue(
resolveField<unknown>(streamDocument, field.name).value
)
);
const validListRootFields = getListSourceRootFields(entityFields)
.map((field) => ({
...field,
displayName:
getEntityFieldDisplayName(field.name, entityFields) ??
field.displayName ??
field.name,
}))
.filter(hasRequiredDescendants)
.filter((field) =>
!streamDocument
? true
: hasListSourceValueInDocument(streamDocument, field.name)
);

return sortFields([
...dedupeFieldsByName(validLinkedEntityRootFields),
...dedupeFieldsByName(validListRootFields),
]);
}

let filteredEntityFields = getFilteredEntityFields(entityFields, filter);

if (filter.directChildrenOf && filteredEntityFields.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ describe("EntityFieldSelectorField", () => {
expect(tooltip).toBeDefined();
expect(
within(tooltip).getByText(
"List elements must be able to satisfy the mapping requirements:"
"List elements should be able to satisfy the mapping requirements:"
)
).toBeDefined();
expect(within(tooltip).getByText("rich_text_v2")).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export const ConstantValueModeToggler = ({
const locale = i18n.language;
const itemSourceParentFieldTooltipTitle = pt(
"itemSourceParentFieldTooltip",
"List elements must be able to satisfy the mapping requirements:"
"List elements should be able to satisfy the mapping requirements:"
);

return (
Expand Down
2 changes: 0 additions & 2 deletions packages/visual-editor/src/utils/cardSlots/mappedSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,4 @@ export type MappedSourceFieldFilter<T extends Record<string, any>> =
RenderEntityFieldFilter<T> & {
/** Higher-priority repeated-source constraints used by itemSource pickers. */
itemSourceTypes?: EntityFieldTypes[][];
/** Used only when itemSourceTypes is not provided. */
mappedSourceTypes?: EntityFieldTypes[][];
};
Loading