Skip to content

Commit

Permalink
[Content | Schema] Block Selector (#3090)
Browse files Browse the repository at this point in the history
Resolves #3052 

### Preview

[screen-recorder-fri-dec-06-2024-12-47-33.webm](https://github.com/user-attachments/assets/68c028ef-2671-477b-93a1-7a710110a9b6)

---------

Co-authored-by: Andres <[email protected]>
  • Loading branch information
finnar-bin and agalin920 authored Jan 15, 2025
1 parent 0c4624d commit 337ac57
Show file tree
Hide file tree
Showing 19 changed files with 747 additions and 20 deletions.
21 changes: 21 additions & 0 deletions cypress/e2e/content/content.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,25 @@ describe("Content Specs", () => {
.should("have.value", "12:00 pm");
});
});

describe("Block Selector Field", () => {
before(() => {
cy.waitOn("/v1/content/models*", () => {
cy.visit("/content/6-556370-8sh47g/7-b939a4-457q19");
});
});

it("Sets a block variant", () => {
cy.getBySelector("BlockSelectorModelField", { timeout: 10000 })
.find("input")
.click();
cy.get(".MuiAutocomplete-popper .MuiAutocomplete-option")
.contains("Test Block Do Not Delete")
.click();

cy.getBySelector("BlockSelectorVariantField", { timeout: 10000 }).click();
cy.getBySelector("Variant_0").click();
cy.getBySelector("BlockFieldVariantPreview").should("exist");
});
});
});
37 changes: 36 additions & 1 deletion cypress/e2e/schema/field.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const SELECTORS = {
FIELD_SELECT_BOOLEAN: "FieldItem_yes_no",
FIELD_SELECT_ONE_TO_ONE: "FieldItem_one_to_one",
FIELD_SELECT_CURRENCY: "FieldItem_currency",
FIELD_SELECT_BLOCK_SELECTOR: "FieldItem_block_selector",
MEDIA_CHECKBOX_LIMIT: "MediaCheckbox_limit",
MEDIA_CHECKBOX_LOCK: "MediaCheckbox_group_id",
DROPDOWN_ADD_OPTION: "DropdownAddOption",
Expand Down Expand Up @@ -322,7 +323,7 @@ describe("Schema: Fields", () => {
// Select a related model
cy.getBySelector(SELECTORS.AUTOCOMPLETE_MODEL_ZUID)
.should("exist")
.type("cypress");
.type("group with visible");
cy.get("[role=listbox] [role=option]").first().click();

cy.wait("@getFields");
Expand Down Expand Up @@ -397,6 +398,40 @@ describe("Schema: Fields", () => {
cy.getBySelector(`Field_${fieldName}`).should("exist");
});

it("Creates a Block field", () => {
cy.intercept("**/fields?showDeleted=true").as("getFields");

const fieldLabel = `Block Selector ${timestamp}`;
const fieldName = `block_selector_${timestamp}`;

// Open the add field modal
cy.getBySelector(SELECTORS.ADD_FIELD_BTN).should("exist").click();
cy.getBySelector(SELECTORS.ADD_FIELD_MODAL).should("exist");

// Select one-to-one relationship field
cy.getBySelector(SELECTORS.FIELD_SELECT_BLOCK_SELECTOR)
.should("exist")
.click();

// Fill up fields
cy.getBySelector(SELECTORS.INPUT_LABEL).should("exist").type(fieldLabel);
cy.get("input[name='label']")
.should("exist")
.should("have.value", fieldLabel);
cy.get("input[name='name']")
.should("exist")
.should("have.value", fieldName);

// Click done
cy.getBySelector(SELECTORS.SAVE_FIELD_BUTTON).should("exist").click();
cy.getBySelector(SELECTORS.ADD_FIELD_MODAL).should("not.exist");

cy.wait("@getFields");

// Check if field exists
cy.getBySelector(`Field_${fieldName}`).should("exist");
});

it("Creates a field via add another field button", () => {
cy.intercept("**/fields?showDeleted=true").as("getFields");

Expand Down
7 changes: 7 additions & 0 deletions src/apps/content-editor/src/app/components/Editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@ export default memo(function Editor({
}
}

if (field.datatype === "block_selector") {
errors[name] = {
...(errors[name] ?? []),
INVALID_BLOCK_VARIANT: false,
};
}

onUpdateFieldErrors(errors);

// Always dispatch the data update
Expand Down
13 changes: 13 additions & 0 deletions src/apps/content-editor/src/app/components/Editor/Field/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { FieldTypeDate } from "../../../../../../../shell/components/FieldTypeDa
import { FieldTypeDateTime } from "../../../../../../../shell/components/FieldTypeDateTime";
import { FieldTypeSort } from "../../../../../../../shell/components/FieldTypeSort";
import { FieldTypeNumber } from "../../../../../../../shell/components/FieldTypeNumber";
import { FieldTypeBlockSelector } from "../../../../../../../shell/components/FieldTypeBlockSelector";

import styles from "./Field.less";
import { MemoryRouter } from "react-router";
Expand Down Expand Up @@ -961,6 +962,18 @@ export const Field = ({
</FieldShell>
);

case "block_selector":
return (
<FieldShell settings={fieldData} errors={errors}>
<FieldTypeBlockSelector
value={value ? value?.toString() : null}
onChange={(value) => onChange(value, name, datatype)}
requiredError={errors?.MISSING_REQUIRED}
missingVariantError={errors?.INVALID_BLOCK_VARIANT}
/>
</FieldShell>
);

default:
return (
<AppLink to={`/schema/${contentModelZUID}/field/${ZUID}`}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type Error = {
REGEX_PATTERN_MISMATCH?: string;
REGEX_RESTRICT_PATTERN_MATCH?: string;
INVALID_RANGE?: string;
INVALID_BLOCK_VARIANT?: boolean;
};

type FieldShellProps = {
Expand Down Expand Up @@ -112,6 +113,10 @@ export const FieldShell = ({
errorMessages.push(errors.CUSTOM_ERROR);
}

if (errors?.INVALID_BLOCK_VARIANT) {
errorMessages.push("Please select a block variant.");
}

if (errorMessages.length === 0) {
return "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const getErrorMessage = (errors: Error) => {
errorMessages.push(errors.CUSTOM_ERROR);
}

if (errors?.INVALID_BLOCK_VARIANT) {
errorMessages.push("Please select a block variant.");
}

return errorMessages;
};

Expand Down
17 changes: 16 additions & 1 deletion src/apps/content-editor/src/app/views/ItemCreate/ItemCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,11 @@ export const ItemCreate = () => {
})
);
if (res.err || res.error) {
if (res.missingRequired || res.lackingCharLength) {
if (
res.missingRequired ||
res.lackingCharLength ||
res.invalidBlockVariantValue
) {
const missingRequiredFieldNames: string[] =
res.missingRequired?.reduce(
(acc: string[], curr: ContentModelField) => {
Expand Down Expand Up @@ -269,6 +273,17 @@ export const ItemCreate = () => {
});
}

if (res.invalidBlockVariantValue?.length) {
res.invalidBlockVariantValue?.forEach(
(field: ContentModelField) => {
errors[field.name] = {
...(errors[field.name] ?? {}),
INVALID_BLOCK_VARIANT: true,
};
}
);
}

setFieldErrors(errors);

// scroll to required field
Expand Down
9 changes: 9 additions & 0 deletions src/apps/content-editor/src/app/views/ItemEdit/ItemEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,15 @@ export default function ItemEdit() {
});
}

if (res.invalidBlockVariantValue?.length) {
res.invalidBlockVariantValue?.forEach((field) => {
errors[field.name] = {
...(errors[field.name] ?? {}),
INVALID_BLOCK_VARIANT: true,
};
});
}

setFieldErrors(errors);
throw new Error(errors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import {
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import CloseIcon from "@mui/icons-material/Close";
import { useSelector } from "react-redux";

import { FieldItem } from "../FieldItem";
import { FieldListData, FIELD_COPY_CONFIG } from "../../configs";
import { isZestyEmail } from "../../../../../../../utility/isZestyEmail";
import { AppState } from "../../../../../../../shell/store/types";
import { User } from "../../../../../../../shell/services/types";

interface Props {
onFieldClick: (fieldType: string, fieldName: string) => void;
onModalClose: () => void;
}
export const FieldSelection = ({ onFieldClick, onModalClose }: Props) => {
const user: User = useSelector((state: AppState) => state.user);
const [fieldTypes, setFieldTypes] = useState(FIELD_COPY_CONFIG);

const handleFilterFields = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -140,18 +145,27 @@ export const FieldSelection = ({ onFieldClick, onModalClose }: Props) => {
rowGap={1.5}
columnGap={2}
>
{fieldTypes[fieldKey].map((field: FieldListData, index) => (
<FieldItem
key={index}
fieldName={field.name}
shortDescription={field.shortDescription}
fieldType={field.type}
description={field.description}
commonUses={field.commonUses}
proTip={field.proTip}
onFieldClick={() => onFieldClick(field.type, field.name)}
/>
))}
{fieldTypes[fieldKey].map((field: FieldListData, index) => {
if (
!isZestyEmail(user.email) &&
field?.type === "block_selector"
) {
return <></>;
}

return (
<FieldItem
key={index}
fieldName={field.name}
shortDescription={field.shortDescription}
fieldType={field.type}
description={field.description}
commonUses={field.commonUses}
proTip={field.proTip}
onFieldClick={() => onFieldClick(field.type, field.name)}
/>
);
})}
</Box>
</Box>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const Rules = ({
formData?.minCharLimit !== null && formData?.maxCharLimit !== null
);

if (type === "uuid") {
if (type === "uuid" || type === "block_selector") {
return <ComingSoon />;
}

Expand Down
7 changes: 6 additions & 1 deletion src/apps/schema/src/app/components/Field/FieldIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import ToggleOnRounded from "@mui/icons-material/ToggleOnRounded";
import KeyboardArrowDownRounded from "@mui/icons-material/KeyboardArrowDownRounded";
import ColorLensRounded from "@mui/icons-material/ColorLensRounded";
import FormatListNumberedRounded from "@mui/icons-material/FormatListNumberedRounded";
import { Markdown, OneToOne } from "@zesty-io/material";
import { Markdown, OneToOne, Block } from "@zesty-io/material";
import { Box } from "@mui/system";
import { SvgIcon } from "@mui/material";

Expand Down Expand Up @@ -67,6 +67,11 @@ const icons: Icons = {
backgroundColor: "pink.50",
borderColor: "pink.600",
},
block_selector: {
icon: Block as SvgIconComponent,
backgroundColor: "pink.50",
borderColor: "pink.600",
},
markdown: {
icon: Markdown as SvgIconComponent,
backgroundColor: "green.50",
Expand Down
19 changes: 18 additions & 1 deletion src/apps/schema/src/app/components/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export type FieldType =
| "files"
| "fontawesome"
| "wysiwyg_advanced"
| "article_writer";
| "article_writer"
| "block_selector"; // TODO: Will need to confirm if this type is already supported by the api
interface FieldListData {
type: FieldType;
name: string;
Expand Down Expand Up @@ -185,6 +186,17 @@ const FIELD_COPY_CONFIG: { [key: string]: FieldListData[] } = {
"You can use External URL fields if you want to link to external websites.",
subHeaderText: "Use this field to link to an internal content item",
},
{
type: "block_selector",
name: "Block Selector",
shortDescription: "Link to a variant of a block model",
description:
"The Block Selector field allows a user to select a unique variant of any block model they would like to see rendered on their page.",
commonUses: ["Footer Section", "Block at the end of article", "Forms"],
proTip:
"These are great to use when you want to use different end blocks at the end of different pages of the same model",
subHeaderText: "Link to a variant of a block model",
},
],
numeric: [
{
Expand Down Expand Up @@ -351,6 +363,7 @@ const TYPE_TEXT: Record<FieldType, string> = {
wysiwyg_advanced: "WYSIWYG (Advanced)",
wysiwyg_basic: "WYSIWYG",
yes_no: "Boolean",
block_selector: "Block Selector",
};

const COMMON_FIELDS: InputField[] = [
Expand Down Expand Up @@ -700,6 +713,10 @@ const FORM_CONFIG: Record<FieldType, FormConfig> = {
],
rules: [...COMMON_RULES],
},
block_selector: {
details: [...COMMON_FIELDS],
rules: [],
},
};

const SYSTEM_FIELDS: readonly SystemField[] = [
Expand Down
1 change: 1 addition & 0 deletions src/apps/schema/src/app/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export const getCategory = (type: string) => {
case "one_to_many":
case "link":
case "internal_link":
case "block_selector":
category = "relationship";
break;

Expand Down
Loading

0 comments on commit 337ac57

Please sign in to comment.