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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 90 additions & 0 deletions packages/visual-editor/src/editor/yextEntityFieldUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,96 @@ describe("getFieldsForSelector", () => {
);
});

it("allows string descendants to satisfy rich text item source requirements", () => {
const fields = getFieldsForSelector(
{
fields: [
{
name: "c_articles",
definition: {
name: "c_articles",
typeName: "c_articles",
isList: true,
type: {},
},
children: {
fields: [
{
name: "title",
definition: {
name: "title",
typeName: "type.string",
type: {},
},
},
],
},
},
],
displayNames: {
c_articles: "Articles",
"c_articles.title": "Articles > Title",
},
},
{
itemSourceTypes: [["type.rich_text_v2"]],
}
);

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

it("applies rich text compatibility to mapped source descendant checks", () => {
const fields = getFieldsForSelector(
{
fields: [
{
name: "c_articles",
definition: {
name: "c_articles",
typeName: "c_articles",
isList: true,
type: {},
},
children: {
fields: [
{
name: "title",
definition: {
name: "title",
typeName: "type.string",
type: {},
},
},
],
},
},
],
displayNames: {
c_articles: "Articles",
"c_articles.title": "Articles > Title",
},
},
{
mappedSourceTypes: [["type.rich_text_v2"]],
}
);

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

it("merges duplicate scoped fields when one has a display name and another has nested children", () => {
const fields = getFieldsForSelector(
{
Expand Down
3 changes: 2 additions & 1 deletion packages/visual-editor/src/editor/yextEntityFieldUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
getFilteredEntityFields,
getCompatibleEntityFieldTypes,
RenderEntityFieldFilter,
} from "../internal/utils/getFilteredEntityFields.ts";
import { StreamFields, YextSchemaField } from "../types/entityFields.ts";
Expand Down Expand Up @@ -353,7 +354,7 @@ export const getFieldsForSelector = (
{ fields: [availableField] },
{
allowList: [availableField.name],
types: requiredTypes,
types: requiredTypes.flatMap(getCompatibleEntityFieldTypes),
}
).length > 0
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,46 @@ describe("EntityFieldSelectorField", () => {
expect(screen.getByText("Linked Location > Name")).toBeDefined();
});

it("shows string fields for rich text selectors", () => {
renderEntityField({
field: {
type: "entityField",
label: "Body",
filter: {
types: ["type.rich_text_v2"],
},
},
value: {
field: "",
constantValue: { defaultValue: { html: "" } },
constantValueEnabled: false,
},
entityFields: {
...defaultEntityFields,
fields: [
...defaultEntityFields.fields,
{
name: "longDescription",
definition: {
name: "longDescription",
typeName: "type.rich_text_v2",
type: {},
},
},
],
displayNames: {
...defaultEntityFields.displayNames,
longDescription: "Long Description",
},
},
});

fireEvent.click(screen.getAllByRole("combobox")[0]);

expect(screen.getByText("Description")).toBeDefined();
expect(screen.getByText("Long Description")).toBeDefined();
});

it("does not show linked entity fields for list-only selectors", () => {
renderEntityField({
field: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ describe("getFilteredEntityFields", () => {
);
});

test("treats string fields as compatible with rich text filters", () => {
const result = getFilteredEntityFields(mockStreamFields, {
types: ["type.rich_text_v2"],
});

expect(result.map((field) => field.name)).toEqual(
expect.arrayContaining([
"c_deliveryPromo.description",
"name",
"additionalHoursText",
])
);
});

test("handles nested fields correctly", () => {
const result = getFilteredEntityFields(mockStreamFields, {
allowList: ["address"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export type EntityFieldTypes =
export type ConstantValueTypes = EntityFieldTypes | "imageOrVideo";

const DEFAULT_DISALLOWED_ENTITY_FIELDS = ["uid", "meta", "slug"];
const ENTITY_FIELD_TYPE_COMPATIBILITY: Partial<
Record<EntityFieldTypes, EntityFieldTypes[]>
> = {
"type.rich_text_v2": ["type.string"],
};

// Populate this with fields that aren't allowed to have subfields.
const TOP_LEVEL_ONLY_FIELD_TYPES: string[] = ["type.hours"];
Expand Down Expand Up @@ -156,6 +161,19 @@ const getEntityTypeToFieldNames = (
}, new Map<string, string[]>());
};

/**
* Returns the set of acceptable schema types for one requested selector type,
* including one-way compatibility used by rich-text pickers.
*/
export const getCompatibleEntityFieldTypes = (
entityFieldType: EntityFieldTypes
): EntityFieldTypes[] => {
return [
entityFieldType,
...(ENTITY_FIELD_TYPE_COMPATIBILITY[entityFieldType] ?? []),
];
};

export const getFilteredEntityFields = <T extends Record<string, any>>(
streamFields: StreamFields | null,
filter: RenderEntityFieldFilter<T>
Expand Down Expand Up @@ -228,9 +246,11 @@ export const getFilteredEntityFields = <T extends Record<string, any>>(
const updatedFilteredEntitySubFields: YextSchemaField[] = [];
filter.types.forEach((type) => {
updatedFilteredEntitySubFields.push(
...filteredEntitySubFields.filter((field) => {
return typeToFieldNames.get(type)?.includes(field.name);
})
...getCompatibleEntityFieldTypes(type).flatMap((compatibleType) =>
filteredEntitySubFields.filter((field) =>
typeToFieldNames.get(compatibleType)?.includes(field.name)
)
)
);
});
filteredEntitySubFields = updatedFilteredEntitySubFields;
Expand Down
Loading