diff --git a/packages/manager/src/managers/telemetry/types.ts b/packages/manager/src/managers/telemetry/types.ts index 9d819501dc..6c1857d2b2 100644 --- a/packages/manager/src/managers/telemetry/types.ts +++ b/packages/manager/src/managers/telemetry/types.ts @@ -45,6 +45,7 @@ export const SegmentEventType = { sharedOnboarding_step_opened: "shared-onboarding:step-opened", sharedOnboarding_step_completed: "shared-onboarding:step-completed", sharedOnboarding_completed: "shared-onboarding:completed", + sharedOnboarding_tutorial: "shared-onboarding:follow-tutorial", } as const; type SegmentEventTypes = (typeof SegmentEventType)[keyof typeof SegmentEventType]; @@ -107,6 +108,8 @@ export const HumanSegmentEventType = { "Prismic Onboarding Guide Step Open", [SegmentEventType.sharedOnboarding_completed]: "Prismic Onboarding Guide Completed", + [SegmentEventType.sharedOnboarding_tutorial]: + "Prismic Onboarding Guide Follow Tutorial", } as const; export type HumanSegmentEventTypes = @@ -387,7 +390,10 @@ type SliceMachineSharedOnboardingCompleted = SegmentEvent< typeof SegmentEventType.sharedOnboarding_completed, SharedOnboardingProperties >; - +type SliceMachineSharedOnboardingTutorial = SegmentEvent< + typeof SegmentEventType.sharedOnboarding_tutorial, + SharedOnboardingProperties +>; type SliceMachinePostPushEmptyStateCtaClicked = SegmentEvent< typeof SegmentEventType.postPush_emptyStateCtaClicked >; @@ -441,6 +447,7 @@ export type SegmentEvents = | SliceMachineSharedOnboardingStepOpened | SliceMachineSharedOnboardingStepCompleted | SliceMachineSharedOnboardingCompleted + | SliceMachineSharedOnboardingTutorial | SliceMachinePostPushEmptyStateCtaClicked | SliceMachinePostPushToastCtaClicked | SliceMachineExperimentExposure; diff --git a/packages/slice-machine/src/legacy/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx b/packages/slice-machine/src/legacy/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx index b291f2d7f9..6f35e0e7c6 100644 --- a/packages/slice-machine/src/legacy/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +++ b/packages/slice-machine/src/legacy/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx @@ -165,10 +165,18 @@ export const CreateCustomTypeModal: React.FC = ({ })} name is already taken.`; } + if (["update", "insert"].includes(label.toLowerCase())) { + errors.label = `Name "${label}" is reserved for Slice Machine use.`; + } + if (!id || !id.length) { errors.id = "ID cannot be empty."; } + if (["update", "insert"].includes(id.toLowerCase())) { + errors.id = `Id "${id}" is reserved for Slice Machine use.`; + } + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!errors.id && id && !API_ID_REGEX.exec(id)) { errors.id = "Invalid id: No special characters allowed."; diff --git a/packages/slice-machine/src/legacy/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx b/packages/slice-machine/src/legacy/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx index ddceea5746..6ad261d8e6 100644 --- a/packages/slice-machine/src/legacy/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +++ b/packages/slice-machine/src/legacy/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx @@ -103,6 +103,10 @@ export const RenameCustomTypeModal: React.FC = ({ })} name is already taken.`; } + if (["update", "insert"].includes(newName.toLowerCase())) { + errors.customTypeName = `Name "${newName}" is reserved for Slice Machine use.`; + } + return Object.keys(errors).length > 0 ? errors : undefined; }} > diff --git a/packages/slice-machine/src/legacy/components/Forms/formsValidator.ts b/packages/slice-machine/src/legacy/components/Forms/formsValidator.ts index 7c884164d1..3d5f304cfd 100644 --- a/packages/slice-machine/src/legacy/components/Forms/formsValidator.ts +++ b/packages/slice-machine/src/legacy/components/Forms/formsValidator.ts @@ -21,21 +21,21 @@ export function validateSliceModalValues( if (!sliceName) { return { sliceName: "Cannot be empty" }; } + if (RESERVED_SLICE_NAME.includes(sliceName.toLowerCase())) { + return { + sliceName: `Name "${sliceName}" is reserved for Slice Machine use.`, + }; + } if (!API_ID_REGEX.exec(sliceName)) { - return { sliceName: "No special characters allowed" }; + return { sliceName: "No special characters allowed." }; } const cased = startCase(camelCase(sliceName)).replace(/\s/gm, ""); if (cased !== sliceName.trim()) { - return { sliceName: "Value has to be PascalCased" }; + return { sliceName: "Value has to be PascalCased." }; } // See: #599 if (sliceName.match(/^\d/)) { - return { sliceName: "Value cannot start with a number" }; - } - if (RESERVED_SLICE_NAME.includes(sliceName)) { - return { - sliceName: `${sliceName} is reserved for Slice Machine use`, - }; + return { sliceName: "Value cannot start with a number." }; } const localNames = localLibs.flatMap((lib) => diff --git a/packages/slice-machine/src/legacy/lib/consts.ts b/packages/slice-machine/src/legacy/lib/consts.ts index cf6b349261..6157f54b6d 100644 --- a/packages/slice-machine/src/legacy/lib/consts.ts +++ b/packages/slice-machine/src/legacy/lib/consts.ts @@ -1,5 +1,5 @@ // A list of slice names that are reserved for internal uses. -export const RESERVED_SLICE_NAME = ["components"]; +export const RESERVED_SLICE_NAME = ["components", "update", "insert"]; export const acceptedImagesTypes = ["png", "jpg", "jpeg"]; diff --git a/playwright/tests/customTypes/customTypesTable.spec.ts b/playwright/tests/customTypes/customTypesTable.spec.ts index c1d15142e0..d95376aac0 100644 --- a/playwright/tests/customTypes/customTypesTable.spec.ts +++ b/playwright/tests/customTypes/customTypesTable.spec.ts @@ -63,6 +63,23 @@ test("I cannot create a custom type with a name or id already used", async ({ ).toBeDisabled(); }); +test("I cannot create a custom type with a name update or insert", async ({ + customTypesTablePage, +}) => { + await customTypesTablePage.goto(); + await customTypesTablePage.openCreateDialog(); + + await expect(customTypesTablePage.createTypeDialog.title).toBeVisible(); + await customTypesTablePage.createTypeDialog.nameInput.fill("update"); + await expect( + customTypesTablePage.createTypeDialog.submitButton, + ).toBeDisabled(); + await customTypesTablePage.createTypeDialog.nameInput.fill("insert"); + await expect( + customTypesTablePage.createTypeDialog.submitButton, + ).toBeDisabled(); +}); + test("I can rename a custom type", async ({ reusableCustomType, customTypesTablePage, diff --git a/playwright/tests/pageTypes/pageTypesTable.spec.ts b/playwright/tests/pageTypes/pageTypesTable.spec.ts index ae20da3c47..2db52aa959 100644 --- a/playwright/tests/pageTypes/pageTypesTable.spec.ts +++ b/playwright/tests/pageTypes/pageTypesTable.spec.ts @@ -89,6 +89,19 @@ test("I cannot create a page type with a name or id already used", async ({ await expect(pageTypesTablePage.createTypeDialog.submitButton).toBeDisabled(); }); +test("I cannot create a page type with a name update or insert", async ({ + pageTypesTablePage, +}) => { + await pageTypesTablePage.goto(); + await pageTypesTablePage.openCreateDialog(); + + await expect(pageTypesTablePage.createTypeDialog.title).toBeVisible(); + await pageTypesTablePage.createTypeDialog.nameInput.fill("update"); + await expect(pageTypesTablePage.createTypeDialog.submitButton).toBeDisabled(); + await pageTypesTablePage.createTypeDialog.nameInput.fill("insert"); + await expect(pageTypesTablePage.createTypeDialog.submitButton).toBeDisabled(); +}); + test("I can rename a page type", async ({ pageTypesTablePage, reusablePageType, diff --git a/playwright/tests/slices/slicesList.spec.ts b/playwright/tests/slices/slicesList.spec.ts index a57228ef1f..95a9e7b667 100644 --- a/playwright/tests/slices/slicesList.spec.ts +++ b/playwright/tests/slices/slicesList.spec.ts @@ -147,6 +147,22 @@ test("I cannot rename a slice with a name starting with a number", async ({ await expect(slicesListPage.renameSliceDialog.submitButton).toBeDisabled(); }); +test("I cannot create a slice with a restricted name ", async ({ + slicesListPage, +}) => { + await slicesListPage.goto(); + await slicesListPage.openCreateDialog(); + + const { nameInput, submitButton } = slicesListPage.createSliceDialog; + + await nameInput.fill("components"); + await expect(submitButton).toBeDisabled(); + await nameInput.fill("update"); + await expect(submitButton).toBeDisabled(); + await nameInput.fill("insert"); + await expect(submitButton).toBeDisabled(); +}); + test("I cannot create two slices with the same name", async ({ sliceBuilderPage, slicesListPage, diff --git a/yarn.lock b/yarn.lock index a030864659..678b256234 100644 --- a/yarn.lock +++ b/yarn.lock @@ -585,12 +585,12 @@ __metadata: linkType: hard "@aws-sdk/types@npm:^3.222.0": - version: 3.714.0 - resolution: "@aws-sdk/types@npm:3.714.0" + version: 3.713.0 + resolution: "@aws-sdk/types@npm:3.713.0" dependencies: "@smithy/types": ^3.7.2 tslib: ^2.6.2 - checksum: 2cc6ea0ec3331a24b5cc6dfba0963ff5a673c7149eadbaf9f9ab8bfdfaa56a7a02a7df80f6921108aca3ef022047f931fd8f36b34bfe6c963e8606f72e068143 + checksum: 4e2d211838da5f64bb04693b0a0ab198e28fa1e1d1e394d96b91c87e07af525211bd88f2b91e9619158b595c23d688f2dc2de44fe2d01e8c21ef9a1d7ba0cece languageName: node linkType: hard