Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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