From c9739242e42b607037527ced46eec3262d8bb579 Mon Sep 17 00:00:00 2001 From: Bohdan Imiela Date: Wed, 18 Dec 2024 15:47:23 +0100 Subject: [PATCH] feat: enable another iteration of onboarding guide experiment --- packages/init/src/SliceMachineInitProcess.ts | 2 +- .../manager/src/managers/telemetry/types.ts | 23 ---- .../features/inAppGuide/InAppGuideContext.tsx | 51 ------- .../features/inAppGuide/InAppGuideDialog.tsx | 94 ------------- .../features/inAppGuide/inAppGuideContent.tsx | 65 --------- .../features/onboarding/OnboardingGuide.tsx | 7 +- .../OnboardingGuide.module.css | 21 --- .../OnboardingProgressStepper.tsx | 98 -------------- .../OnboardingProvider.tsx | 127 ------------------ .../OnboardingStepDialog.tsx | 65 --------- .../OnboardingStepDialogContent.tsx | 34 ----- .../OnboardingStepDialog/index.ts | 1 - .../OnboardingTutorial/OnboardingTutorial.tsx | 25 ---- .../SliceMachineOnboardingGuide.tsx | 74 ---------- .../SliceMachineOnboardingGuide/content.tsx | 94 ------------- .../SliceMachineOnboardingGuide/types.ts | 26 ---- .../onboarding/useOnboardingExperiment.ts | 6 - .../useSharedOnboardingExperiment.ts | 2 +- .../components/ReviewModal/ReviewModal.tsx | 7 - packages/slice-machine/src/pages/_app.tsx | 19 +-- playwright/fixtures/index.ts | 4 - playwright/pages/SliceMachinePage.ts | 3 - .../pages/components/InAppGuideDialog.ts | 38 ------ playwright/tests/common/inAppGuide.spec.ts | 36 ----- playwright/tests/common/reviewForm.spec.ts | 37 ----- 25 files changed, 8 insertions(+), 951 deletions(-) delete mode 100644 packages/slice-machine/src/features/inAppGuide/InAppGuideContext.tsx delete mode 100644 packages/slice-machine/src/features/inAppGuide/InAppGuideDialog.tsx delete mode 100644 packages/slice-machine/src/features/inAppGuide/inAppGuideContent.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingGuide.module.css delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProgressStepper.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialog.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialogContent.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/index.ts delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingTutorial/OnboardingTutorial.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/SliceMachineOnboardingGuide.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/content.tsx delete mode 100644 packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/types.ts delete mode 100644 packages/slice-machine/src/features/onboarding/useOnboardingExperiment.ts delete mode 100644 playwright/pages/components/InAppGuideDialog.ts delete mode 100644 playwright/tests/common/inAppGuide.spec.ts diff --git a/packages/init/src/SliceMachineInitProcess.ts b/packages/init/src/SliceMachineInitProcess.ts index a42127cd63..e9abae4516 100644 --- a/packages/init/src/SliceMachineInitProcess.ts +++ b/packages/init/src/SliceMachineInitProcess.ts @@ -898,7 +898,7 @@ ${chalk.cyan("?")} Your Prismic repository name`.replace("\n", ""), try { const { value: onboardingExperimentVariant } = (await this.manager.telemetry.getExperimentVariant( - "shared-onboarding", + "shared-onboarding-new", )) ?? {}; if (onboardingExperimentVariant === "with-shared-onboarding") { this.manager.prismicRepository.completeOnboardingStep( diff --git a/packages/manager/src/managers/telemetry/types.ts b/packages/manager/src/managers/telemetry/types.ts index 9d819501dc..166d187544 100644 --- a/packages/manager/src/managers/telemetry/types.ts +++ b/packages/manager/src/managers/telemetry/types.ts @@ -39,9 +39,6 @@ export const SegmentEventType = { postPush_emptyStateCtaClicked: "post-push:empty-state-cta-clicked", postPush_toastCtaClicked: "post-push:toast-cta-clicked", experiment_exposure: "experiment:exposure", - onboarding_step_opened: "onboarding:step-opened", - onboarding_step_completed: "onboarding:step-completed", - onboarding_completed: "onboarding:completed", sharedOnboarding_step_opened: "shared-onboarding:step-opened", sharedOnboarding_step_completed: "shared-onboarding:step-completed", sharedOnboarding_completed: "shared-onboarding:completed", @@ -96,11 +93,6 @@ export const HumanSegmentEventType = { [SegmentEventType.postPush_toastCtaClicked]: "SliceMachine Post Push Toast CTA Clicked", [SegmentEventType.experiment_exposure]: "$exposure", - [SegmentEventType.onboarding_step_opened]: - "SliceMachine Onboarding Step Opened", - [SegmentEventType.onboarding_step_completed]: - "SliceMachine Onboarding Step Completed", - [SegmentEventType.onboarding_completed]: "SliceMachine Onboarding Completed", [SegmentEventType.sharedOnboarding_step_completed]: "Prismic Onboarding Guide Step Completed", [SegmentEventType.sharedOnboarding_step_opened]: @@ -363,18 +355,6 @@ type SharedOnboardingProperties> = T & { source: "SliceMachine"; }; -type SliceMachineOnboardingStepOpened = SegmentEvent< - typeof SegmentEventType.onboarding_step_opened, - OnboardingCommonPayload ->; -type SliceMachineOnboardingStepCompleted = SegmentEvent< - typeof SegmentEventType.onboarding_step_completed, - OnboardingCommonPayload ->; -type SliceMachineOnboardingCompleted = SegmentEvent< - typeof SegmentEventType.onboarding_completed ->; - type SliceMachineSharedOnboardingStepOpened = SegmentEvent< typeof SegmentEventType.sharedOnboarding_step_opened, SharedOnboardingProperties @@ -435,9 +415,6 @@ export type SegmentEvents = | SliceMachineStart | SliceLibraryBetaModalOpened | SliceLibraryBetaCodeOpened - | SliceMachineOnboardingStepOpened - | SliceMachineOnboardingStepCompleted - | SliceMachineOnboardingCompleted | SliceMachineSharedOnboardingStepOpened | SliceMachineSharedOnboardingStepCompleted | SliceMachineSharedOnboardingCompleted diff --git a/packages/slice-machine/src/features/inAppGuide/InAppGuideContext.tsx b/packages/slice-machine/src/features/inAppGuide/InAppGuideContext.tsx deleted file mode 100644 index 0d88689296..0000000000 --- a/packages/slice-machine/src/features/inAppGuide/InAppGuideContext.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { - createContext, - Dispatch, - FC, - PropsWithChildren, - SetStateAction, - useContext, - useMemo, -} from "react"; - -import { useOnboardingExperiment } from "@/features/onboarding/useOnboardingExperiment"; -import { usePersistedState } from "@/hooks/usePersistedState"; - -type InAppGuideContextValue = { - isInAppGuideOpen: boolean; - setIsInAppGuideOpen: Dispatch>; -}; - -export const InAppGuideContext = createContext< - InAppGuideContextValue | undefined ->(undefined); - -export const InAppGuideProvider: FC = (props) => { - const { children } = props; - const { eligible: isNewOnboardingEnabled } = useOnboardingExperiment(); - const [isInAppGuideOpen, setIsInAppGuideOpen] = usePersistedState( - "isInAppGuideOpen", - true, - ); - - const memoizedValue = useMemo(() => { - if (isNewOnboardingEnabled) { - return { isInAppGuideOpen: false, setIsInAppGuideOpen: () => undefined }; - } - return { isInAppGuideOpen, setIsInAppGuideOpen }; - }, [isInAppGuideOpen, setIsInAppGuideOpen, isNewOnboardingEnabled]); - - return ( - - {children} - - ); -}; - -export const useInAppGuide = () => { - const context = useContext(InAppGuideContext); - if (context === undefined) { - throw new Error("useInAppGuide must be used within an InAppGuideProvider"); - } - return context; -}; diff --git a/packages/slice-machine/src/features/inAppGuide/InAppGuideDialog.tsx b/packages/slice-machine/src/features/inAppGuide/InAppGuideDialog.tsx deleted file mode 100644 index a9dd1da276..0000000000 --- a/packages/slice-machine/src/features/inAppGuide/InAppGuideDialog.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { - Box, - Dialog, - DialogContent, - DialogHeader, - IconButton, - ScrollArea, - Separator, - Text, - Video, -} from "@prismicio/editor-ui"; -import { FC } from "react"; - -import { Count } from "@/components/Count"; -import { useOnboardingExperiment } from "@/features/onboarding/useOnboardingExperiment"; -import { useIsEmptyProject } from "@/hooks/useIsEmptyProject"; -import { HelpIcon } from "@/icons/HelpIcon"; - -import { useInAppGuideContent } from "./inAppGuideContent"; -import { useInAppGuide } from "./InAppGuideContext"; - -export const InAppGuideDialog: FC = () => { - const isEmptyProject = useIsEmptyProject(); - const { isInAppGuideOpen, setIsInAppGuideOpen } = useInAppGuide(); - const inAppGuideContent = useInAppGuideContent(); - - const { eligible: newOnboardingEnabled } = useOnboardingExperiment(); - - const trigger = - !isEmptyProject && !newOnboardingEnabled ? ( - - } - onClick={() => { - setIsInAppGuideOpen(!isInAppGuideOpen); - }} - radius="full" - variant="solid" - /> - - ) : undefined; - - return ( - { - setIsInAppGuideOpen(open); - }} - position="bottomRight" - size={{ - width: 360, - height: 456, - }} - > - - - - - {inAppGuideContent.description} - - - - - - - {inAppGuideContent.steps.map((content, index) => ( - - - - {index + 1} - - {content.title} - - - - - - - ))} - - - {inAppGuideContent.successTitle} - {inAppGuideContent.successDescription} - - - - - ); -}; diff --git a/packages/slice-machine/src/features/inAppGuide/inAppGuideContent.tsx b/packages/slice-machine/src/features/inAppGuide/inAppGuideContent.tsx deleted file mode 100644 index 794fcc5f68..0000000000 --- a/packages/slice-machine/src/features/inAppGuide/inAppGuideContent.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Text } from "@prismicio/editor-ui"; - -import { useRepositoryInformation } from "@/hooks/useRepositoryInformation"; - -export function useInAppGuideContent() { - const { repositoryUrl } = useRepositoryInformation(); - - return { - title: "Build a page in 5 minutes", - description: - "Great, now that you have a page type, let's make it a live page!", - steps: [ - { - title: "Add slices to your page type", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/ADD_SLICE.mp4", - description: "Use slice templates and add them to your page type.", - }, - { - title: "Code your page", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/CODE_SNIP.mp4", - description: - "If you don't already have a page component, copy-paste the page snippets provided in your page type to create one.", - }, - { - title: "Push to your Page Builder", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PUSH.mp4", - description: - "You have just created some models, but you can't use them yet. First, you must push them to the Page Builder. The Page Builder is where you create content. Go head — push your models.", - }, - { - title: "Create content", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/WRITE.mp4", - description: ( - <> - Open your{" "} - - Page Builder - - , create a page, add slices, save, and publish. Then, come back - here. - - ), - }, - { - title: "Render your page", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PREVIEW.mp4", - description: ( - <> - To render the page, run your project in your terminal and visit the - page on localhost (e.g.{" "} - localhost:3000/example-page). - - ), - }, - ], - successTitle: "Next", - successDescription: - "Create more slices and then go back to the Page Builder to build out your website.", - }; -} diff --git a/packages/slice-machine/src/features/onboarding/OnboardingGuide.tsx b/packages/slice-machine/src/features/onboarding/OnboardingGuide.tsx index 39459f6257..1c9e908293 100644 --- a/packages/slice-machine/src/features/onboarding/OnboardingGuide.tsx +++ b/packages/slice-machine/src/features/onboarding/OnboardingGuide.tsx @@ -1,10 +1,8 @@ import { useMediaQuery } from "@prismicio/editor-ui"; -import { useOnboardingExperiment } from "@/features/onboarding/useOnboardingExperiment"; import { useUpdateAvailable } from "@/hooks/useUpdateAvailable"; import { SharedOnboardingGuide } from "./SharedOnboardingGuide"; -import { SliceMachineOnboardingGuide } from "./SliceMachineOnboardingGuide/SliceMachineOnboardingGuide"; import { useSharedOnboardingExperiment } from "./useSharedOnboardingExperiment"; export function OnboardingGuide() { @@ -12,18 +10,15 @@ export function OnboardingGuide() { const isSharedExperimentEligible = useSharedOnboardingExperiment().eligible; if (!isVisible) return null; - if (isSharedExperimentEligible) return ; - return ; + return isSharedExperimentEligible ? : null; } function useIsOnboardingGuideVisible() { const isMediaQueryVisible = useMediaQuery({ min: "medium" }); - const isExperimentEligible = useOnboardingExperiment().eligible; const updates = useUpdateAvailable(); return ( isMediaQueryVisible && - isExperimentEligible && !updates.sliceMachineUpdateAvailable && !updates.adapterUpdateAvailable ); diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingGuide.module.css b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingGuide.module.css deleted file mode 100644 index 61f6f423ec..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingGuide.module.css +++ /dev/null @@ -1,21 +0,0 @@ -.container { - overflow: hidden; - position: relative; -} - -.confettiCannon { - position: absolute; - bottom: 0; - left: 0; -} - -.visible { - opacity: 1; -} - -.invisible { - opacity: 0; - /* A delay of 2s gives room for a 3s confetti animation to happen and then - fade out while the it's ending */ - transition: opacity 500ms linear 2s; -} diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProgressStepper.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProgressStepper.tsx deleted file mode 100644 index 2a49a422d3..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProgressStepper.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuTrigger, - Icon, - Text, -} from "@prismicio/editor-ui"; -import { useState } from "react"; - -import { telemetry } from "@/apiClient"; - -import { useOnboardingContext } from "./OnboardingProvider"; -import { OnboardingStepDialog } from "./OnboardingStepDialog"; -import type { OnboardingStep } from "./types"; - -const EndCtaIcon = () => ; - -interface OnboardingProgressStepperProps { - buttonSize?: "large" | "medium"; -} -export function OnboardingProgressStepper( - props: OnboardingProgressStepperProps, -) { - const { buttonSize = "medium" } = props; - const { completedStepCount, steps, isStepComplete, isComplete } = - useOnboardingContext(); - - const [isListOpen, setListOpen] = useState(false); - const [isDialogOpen, setDialogOpen] = useState(false); - const [activeStep, setActiveStep] = useState(steps[0]); - - const showStep = (step: OnboardingStep) => { - setActiveStep(step); - setDialogOpen(true); - void telemetry.track({ - event: "onboarding:step-opened", - stepId: step.id, - stepTitle: step.title, - }); - }; - - return ( - <> - setDialogOpen(false)} - /> - - - - - {/* The sideOffset is used to align the list with the bottom of the onboarding card */} - -
setListOpen(false)}> - - - Progress - - - {steps.map((step, index) => { - const isCompleted = isStepComplete(step); - - return ( - showStep(step)} - description={step.description} - completed={isCompleted} - readOnly={step.defaultCompleted} - endAdornment={ - step.defaultCompleted !== true && ( - - ) - } - > - {`${index + 1} ${step.title}`} - - ); - })} -
-
-
- - ); -} diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider.tsx deleted file mode 100644 index bf978c6ede..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { createContext, ReactNode, useContext } from "react"; - -import { telemetry } from "@/apiClient"; -import { onboardingSteps } from "@/features/onboarding/SliceMachineOnboardingGuide/content"; -import { - type OnboardingStep, - type OnboardingStepId, - type OnboardingStepStatuses, - onboardingStepStatusesSchema, -} from "@/features/onboarding/SliceMachineOnboardingGuide/types"; -import { usePersistedState } from "@/hooks/usePersistedState"; - -type OnboardingContext = { - steps: OnboardingStep[]; - completedStepCount: number; - toggleStepComplete: (step: OnboardingStep) => void; - getStepIndex: (step: OnboardingStepId) => number; - isStepComplete: (step: OnboardingStep) => boolean; - isComplete: boolean; -}; - -export const OnboardingContext = createContext( - undefined, -); - -const getInitialState = (steps: OnboardingStep[]): OnboardingStepStatuses => { - // if the old guide was dismissed, all steps start as complete - const wasOldGuideDismissed = - localStorage.getItem("slice-machine_isInAppGuideOpen") === "false"; - - return Object.fromEntries( - steps.map((step) => [ - step.id, - step.defaultCompleted ?? wasOldGuideDismissed, - ]), - ) as OnboardingStepStatuses; -}; - -type OnboardingProviderProps = { - children: ReactNode; - onComplete?: () => void; -}; - -export const OnboardingProvider = ({ - children, - onComplete, -}: OnboardingProviderProps) => { - const steps = onboardingSteps; - const [stepStatus, setStepStatus] = useStepStatus(); - - const toggleStepComplete = (step: OnboardingStep) => { - const newCompleteState = !isStepComplete(step); - const nextState = { ...stepStatus, [step.id]: newCompleteState }; - setStepStatus(nextState); - - if (newCompleteState) { - void telemetry.track({ - event: "onboarding:step-completed", - stepId: step.id, - stepTitle: step.title, - }); - } - if (Object.values(nextState).every(Boolean)) { - onComplete?.(); - void telemetry.track({ - event: "onboarding:completed", - }); - } - }; - - const getStepIndex = (stepId: OnboardingStepId) => { - return steps.findIndex(({ id }) => id === stepId); - }; - - const isStepComplete = (step: OnboardingStep) => { - return Boolean(stepStatus[step.id]) || Boolean(step.defaultCompleted); - }; - - const completedStepCount = steps.filter((step) => - isStepComplete(step), - ).length; - - return ( - - {children} - - ); -}; - -function useStepStatus() { - return usePersistedState( - "onboardingSteps", - getInitialState(onboardingSteps), - { schema: onboardingStepStatusesSchema }, - ); -} - -export function useIsOnboardingCompleted() { - const [stepStatus] = useStepStatus(); - - const completedStepCount = onboardingSteps.filter( - (step) => Boolean(stepStatus[step.id]) || Boolean(step.defaultCompleted), - ).length; - - return completedStepCount === onboardingSteps.length; -} - -export const useOnboardingContext = () => { - const context = useContext(OnboardingContext); - - if (context == null) { - throw new Error( - "useOnboardingContext must be used within an OnboardingProvider", - ); - } - - return context; -}; diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialog.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialog.tsx deleted file mode 100644 index eb62c47d92..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialog.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Dialog, - DialogActionButton, - DialogActions, - DialogCancelButton, - DialogContent, - DialogHeader, -} from "@prismicio/editor-ui"; -import { useState } from "react"; - -import { useOnboardingContext } from "@/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider"; -import type { OnboardingStep } from "@/features/onboarding/SliceMachineOnboardingGuide/types"; - -import { OnboardingStepDialogContent } from "./OnboardingStepDialogContent"; - -type OnboardingStepDialogProps = { - step: OnboardingStep; - isOpen: boolean; - onClose: () => void; -}; - -export const OnboardingStepDialog = ({ - step, - isOpen, - onClose, -}: OnboardingStepDialogProps) => { - const { toggleStepComplete, isStepComplete } = useOnboardingContext(); - const [ctaOkText, setCtaOkText] = useState(getCtaOkText); - - function getCtaOkText() { - return isStepComplete(step) ? "Undo step" : "Mark as done"; - } - - const markAsDone = () => { - if (!isOpen) return; - toggleStepComplete(step); - onClose(); - }; - - const updateCtaOkText = () => { - if (!isOpen) return; - setCtaOkText(getCtaOkText()); - }; - - return ( - - - - - - Close - - {ctaOkText} - - - - - ); -}; diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialogContent.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialogContent.tsx deleted file mode 100644 index 10516a40d1..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/OnboardingStepDialogContent.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Box, ScrollArea, Text, Video } from "@prismicio/editor-ui"; - -import { useOnboardingContext } from "@/features/onboarding/SliceMachineOnboardingGuide/OnboardingProvider"; -import { OnboardingStep } from "@/features/onboarding/SliceMachineOnboardingGuide/types"; - -type OnboardingStepDialogContentProps = { - step: OnboardingStep; -}; - -export function OnboardingStepDialogContent( - props: OnboardingStepDialogContentProps, -) { - const { step } = props; - const { getStepIndex } = useOnboardingContext(); - - const { content: Content, videoUrl, title = step.title } = step; - - return ( - - -
- - Step {getStepIndex(step.id) + 1} - - {title} - {Content && } -
- {typeof videoUrl == "string" && ( -
-
- ); -} diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/index.ts b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/index.ts deleted file mode 100644 index b7a22ec117..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingStepDialog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OnboardingStepDialog } from "./OnboardingStepDialog"; diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingTutorial/OnboardingTutorial.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingTutorial/OnboardingTutorial.tsx deleted file mode 100644 index f5d6108e4f..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/OnboardingTutorial/OnboardingTutorial.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Box, Icon, Text } from "@prismicio/editor-ui"; - -import { useMarketingContent } from "@/hooks/useMarketingContent"; - -export function OnboardingTutorial() { - const { tutorial } = useMarketingContent(); - - const url = tutorial?.url; - - if (url === undefined) return null; - - return ( - - - Or watch our full course - - - - ); -} diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/SliceMachineOnboardingGuide.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/SliceMachineOnboardingGuide.tsx deleted file mode 100644 index 994aa4a80d..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/SliceMachineOnboardingGuide.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useConfetti } from "@prismicio/editor-support/Animation"; -import { - Card, - CardContent, - ProgressBar, - Text, - useMediaQuery, -} from "@prismicio/editor-ui"; -import { useState } from "react"; - -import styles from "./OnboardingGuide.module.css"; -import { OnboardingProgressStepper } from "./OnboardingProgressStepper"; -import { - OnboardingProvider, - useIsOnboardingCompleted, - useOnboardingContext, -} from "./OnboardingProvider"; -import { OnboardingTutorial } from "./OnboardingTutorial/OnboardingTutorial"; - -export function SliceMachineOnboardingGuide() { - const isComplete = useIsOnboardingCompleted(); - const [isVisible, setVisible] = useState(!isComplete); - const confetti = useConfetti({ onAnimationEnd: () => setVisible(false) }); - - if (!isVisible) return null; - - return ( - -
- -
-
- - ); -} - -function OnboardingGuideCard() { - const { steps, completedStepCount, isComplete } = useOnboardingContext(); - const isMediumScreen = useMediaQuery({ min: "medium" }); - - if (!isMediumScreen) return null; - - return ( -
- - -
- - {`Build your first Prismic Page in ${steps.length.toString()} simple steps`} - - - Render a live page with content coming from Prismic in 5 mins - -
- `${value}/${max}`} - /> - - -
-
-
- ); -} diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/content.tsx b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/content.tsx deleted file mode 100644 index ac71b31e61..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/content.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { Text } from "@prismicio/editor-ui"; - -import type { OnboardingStep } from "./types"; - -export const onboardingSteps: OnboardingStep[] = [ - { - id: "createProject", - title: "Create your Prismic website", - description: "Create and set up your project", - defaultCompleted: true, - }, - { - id: "createPageType", - title: "Create your first Page Type", - description: "Build the structure of your page", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918320/DEV_TOOLS/ONBOARDING_GUIDE/Create_page_type_xdn13j.mp4", - content: () => ( - - A page type is a base content structure editors will use to create pages - in the Page Builder (Prismic's content creation UI). A page type can be - reusable (e.g., for multiple blog posts) or single (e.g., for a one-time - page like the homepage). - - ), - }, - { - id: "codePage", - title: "Code your Page", - description: "Prepare code to fetch content", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918320/DEV_TOOLS/ONBOARDING_GUIDE/Step2_code_your_page_jatiur.mp4", - content: () => ( - Prepare your code to query the content from the Prismic API. - ), - }, - { - id: "createSlice", - title: "Create your first Slice", - description: "Build a reusable section", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918322/DEV_TOOLS/ONBOARDING_GUIDE/Step3_add_slice_qzmvxf.mp4", - content: () => ( - - Slices are website sections that can be reused on different pages with - different content. Each slice has a code component automatically - generated by Slice Machine. Start with a template and look at your code - to see how it's structured. - - ), - }, - { - id: "reviewAndPush", - title: "Review & push changes", - description: "Enable editors to create content", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918320/DEV_TOOLS/ONBOARDING_GUIDE/DevTools_Squad_push_changes_ovvmul.mp4", - content: () => ( - - Your page types and slices currently exist only in your local project. - Push them to your repository to make them available for your content - editors. - - ), - }, - { - id: "createContent", - title: "Create content for your page", - description: "Publish your page to the API", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918324/DEV_TOOLS/ONBOARDING_GUIDE/Cre%CC%81er_contenu_e%CC%81tape_4_z9vlzt.mp4", - content: () => ( - - In Prismic, content creation takes place in the Prismic Page Builder UI. - Open the Page Builder and start creating your first page. - - ), - }, - { - id: "renderPage", - title: "Preview your page", - description: "View your page in the browser", - videoUrl: - "https://res.cloudinary.com/dmtf1daqp/video/upload/v1721918321/DEV_TOOLS/ONBOARDING_GUIDE/Step_6_Render_Page_tnauh9.mp4", - content: () => ( - - Now that your content is published, you can run your project in your - terminal and visit the page on your local server (e.g.,{" "} - localhost:3000/example-page). Your content - should now be visible! - - ), - }, -]; diff --git a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/types.ts b/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/types.ts deleted file mode 100644 index 6d2edcb022..0000000000 --- a/packages/slice-machine/src/features/onboarding/SliceMachineOnboardingGuide/types.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { z } from "zod"; - -export const onboardingStepStatusesSchema = z.object({ - createProject: z.boolean().optional(), - createPageType: z.boolean(), - codePage: z.boolean(), - createSlice: z.boolean(), - reviewAndPush: z.boolean(), - createContent: z.boolean(), - renderPage: z.boolean(), -}); - -export type OnboardingStepStatuses = z.infer< - typeof onboardingStepStatusesSchema ->; - -export type OnboardingStepId = keyof OnboardingStepStatuses; - -export interface OnboardingStep { - id: OnboardingStepId; - title: string; - description: string; - content?: () => JSX.Element; - videoUrl?: string; - defaultCompleted?: boolean; -} diff --git a/packages/slice-machine/src/features/onboarding/useOnboardingExperiment.ts b/packages/slice-machine/src/features/onboarding/useOnboardingExperiment.ts deleted file mode 100644 index c2cf40f4c5..0000000000 --- a/packages/slice-machine/src/features/onboarding/useOnboardingExperiment.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useExperimentVariant } from "@/hooks/useExperimentVariant"; - -export const useOnboardingExperiment = () => { - const variant = useExperimentVariant("slicemachine-onboarding"); - return { eligible: variant?.value === "on" }; -}; diff --git a/packages/slice-machine/src/features/onboarding/useSharedOnboardingExperiment.ts b/packages/slice-machine/src/features/onboarding/useSharedOnboardingExperiment.ts index 17a6e11105..f4c49ead21 100644 --- a/packages/slice-machine/src/features/onboarding/useSharedOnboardingExperiment.ts +++ b/packages/slice-machine/src/features/onboarding/useSharedOnboardingExperiment.ts @@ -1,6 +1,6 @@ import { useExperimentVariant } from "@/hooks/useExperimentVariant"; export const useSharedOnboardingExperiment = () => { - const variant = useExperimentVariant("shared-onboarding"); + const variant = useExperimentVariant("shared-onboarding-new"); return { eligible: variant?.value === "with-shared-onboarding" }; }; diff --git a/packages/slice-machine/src/legacy/components/ReviewModal/ReviewModal.tsx b/packages/slice-machine/src/legacy/components/ReviewModal/ReviewModal.tsx index a5ef1ab643..7401e9225e 100644 --- a/packages/slice-machine/src/legacy/components/ReviewModal/ReviewModal.tsx +++ b/packages/slice-machine/src/legacy/components/ReviewModal/ReviewModal.tsx @@ -1,7 +1,6 @@ import { FC } from "react"; import { useSelector } from "react-redux"; -import { useInAppGuide } from "@/features/inAppGuide/InAppGuideContext"; import { hasLocal } from "@/legacy/lib/models/common/ModelData"; import { selectAllCustomTypes } from "@/modules/availableCustomTypes"; import { getLibraries } from "@/modules/slices"; @@ -11,7 +10,6 @@ import { SliceMachineStoreType } from "@/redux/type"; import { ReviewForm } from "./ReviewForm"; export const ReviewModal: FC = () => { - const { isInAppGuideOpen } = useInAppGuide(); const { userReview, customTypes, libraries, lastSyncChange } = useSelector( (store: SliceMachineStoreType) => ({ userReview: getUserReview(store), @@ -21,11 +19,6 @@ export const ReviewModal: FC = () => { }), ); - // Opt out directly if the in-app guide is open - if (isInAppGuideOpen) { - return null; - } - const sliceCount = // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions libraries && libraries.length diff --git a/packages/slice-machine/src/pages/_app.tsx b/packages/slice-machine/src/pages/_app.tsx index 6a410fb2f6..446574a8cf 100644 --- a/packages/slice-machine/src/pages/_app.tsx +++ b/packages/slice-machine/src/pages/_app.tsx @@ -34,8 +34,6 @@ import { ThemeProvider as ThemeUIThemeProvider, useThemeUI } from "theme-ui"; import { getState } from "@/apiClient"; import { ErrorBoundary } from "@/ErrorBoundary"; -import { InAppGuideProvider } from "@/features/inAppGuide/InAppGuideContext"; -import { InAppGuideDialog } from "@/features/inAppGuide/InAppGuideDialog"; import { AutoSyncProvider } from "@/features/sync/AutoSyncProvider"; import { RouteChangeProvider } from "@/hooks/useRouteChange"; import SliceMachineApp from "@/legacy/components/App"; @@ -146,18 +144,11 @@ function App({ > }> - - - - - - - - - - - - + + + + + diff --git a/playwright/fixtures/index.ts b/playwright/fixtures/index.ts index ad4bde98aa..7ce051a236 100644 --- a/playwright/fixtures/index.ts +++ b/playwright/fixtures/index.ts @@ -245,10 +245,6 @@ export const test = baseTest.extend({ })); const newStorage = onboarded ? [ - { - name: `${SLICE_MACHINE_STORAGE_PREFIX}_isInAppGuideOpen`, - value: "false", - }, { name: `${SLICE_MACHINE_STORAGE_PREFIX}_staticFieldsInfoDialogDismissed`, value: "true", diff --git a/playwright/pages/SliceMachinePage.ts b/playwright/pages/SliceMachinePage.ts index 5ca601abcf..69f8320896 100644 --- a/playwright/pages/SliceMachinePage.ts +++ b/playwright/pages/SliceMachinePage.ts @@ -2,14 +2,12 @@ import { Locator, Page, expect } from "@playwright/test"; import { Menu } from "./components/Menu"; import { ReviewDialog } from "./components/ReviewDialog"; -import { InAppGuideDialog } from "./components/InAppGuideDialog"; import { LoginDialog } from "./components/LoginDialog"; export class SliceMachinePage { readonly page: Page; readonly menu: Menu; readonly reviewDialog: ReviewDialog; - readonly inAppGuideDialog: InAppGuideDialog; readonly loginDialog: LoginDialog; readonly body: Locator; readonly breadcrumb: Locator; @@ -21,7 +19,6 @@ export class SliceMachinePage { this.page = page; this.menu = new Menu(page); this.reviewDialog = new ReviewDialog(page); - this.inAppGuideDialog = new InAppGuideDialog(page); this.loginDialog = new LoginDialog(page); /** diff --git a/playwright/pages/components/InAppGuideDialog.ts b/playwright/pages/components/InAppGuideDialog.ts deleted file mode 100644 index 27d9d6bfd6..0000000000 --- a/playwright/pages/components/InAppGuideDialog.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Locator, Page } from "@playwright/test"; - -import { Dialog } from "./Dialog"; - -export class InAppGuideDialog extends Dialog { - override readonly closeButton: Locator; - - constructor(page: Page) { - super(page, { - title: "Build a page in 5 minutes", - }); - - /** - * Components - */ - // Handle components here - - /** - * Static locators - */ - this.closeButton = this.dialog.getByRole("button"); - } - - /** - * Dynamic locators - */ - // Handle dynamic locators here - - /** - * Actions - */ - // Handle actions here - - /** - * Assertions - */ - // Handle assertions here -} diff --git a/playwright/tests/common/inAppGuide.spec.ts b/playwright/tests/common/inAppGuide.spec.ts deleted file mode 100644 index 4214cf2d42..0000000000 --- a/playwright/tests/common/inAppGuide.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect } from "@playwright/test"; - -import { test } from "../../fixtures"; - -test.use({ onboarded: false }); - -test("I can see the in-app guide open by default", async ({ - sliceMachinePage, -}) => { - await sliceMachinePage.gotoDefaultPage(); - - await expect(sliceMachinePage.inAppGuideDialog.title).toBeVisible(); -}); - -test("I can close the in-app guide", async ({ sliceMachinePage }) => { - await sliceMachinePage.gotoDefaultPage(); - - await expect(sliceMachinePage.inAppGuideDialog.title).toBeVisible(); - await sliceMachinePage.inAppGuideDialog.closeButton.click(); - await expect(sliceMachinePage.inAppGuideDialog.title).not.toBeVisible(); - - await sliceMachinePage.page.reload(); - await expect(sliceMachinePage.inAppGuideDialog.title).not.toBeVisible(); -}); - -test("I can see the in-app guide on different pages", async ({ - sliceMachinePage, - slicesListPage, - customTypesTablePage, -}) => { - await slicesListPage.goto(); - await expect(sliceMachinePage.inAppGuideDialog.title).toBeVisible(); - - await customTypesTablePage.goto(); - await expect(sliceMachinePage.inAppGuideDialog.title).toBeVisible(); -}); diff --git a/playwright/tests/common/reviewForm.spec.ts b/playwright/tests/common/reviewForm.spec.ts index c4f3a04067..13bba02fd0 100644 --- a/playwright/tests/common/reviewForm.spec.ts +++ b/playwright/tests/common/reviewForm.spec.ts @@ -10,37 +10,6 @@ test.use({ }, }); -test("I can write a review after onboarding", async ({ - sliceMachinePage, - procedures, -}) => { - const libraries = generateLibraries({ slicesCount: 1 }); - - // We mock a page type with a slice that is a requirement for the review dialog - procedures.mock("getState", ({ data }) => ({ - ...(data as Record), - libraries, - customTypes: generateTypes({ typesCount: 1, libraries }), - remoteCustomTypes: [], - remoteSlices: [], - clientError: undefined, - })); - - await sliceMachinePage.gotoDefaultPage(); - - // We close the in app guide to display the review dialog - await sliceMachinePage.inAppGuideDialog.closeButton.click(); - - await sliceMachinePage.reviewDialog.submitReview({ - rating: 4, - message: "Great job!", - }); - - // We verify that the review dialog is not displayed anymore - await sliceMachinePage.page.reload(); - await expect(sliceMachinePage.reviewDialog.title).not.toBeVisible(); -}); - test("I can write a review after creating enough models", async ({ sliceMachinePage, procedures, @@ -63,9 +32,6 @@ test("I can write a review after creating enough models", async ({ await sliceMachinePage.gotoDefaultPage(); - // We close the in app guide so the review dialog can be displayed - await sliceMachinePage.inAppGuideDialog.closeButton.click(); - // We close the first review for onboarding await sliceMachinePage.reviewDialog.closeButton.click(); @@ -109,9 +75,6 @@ test("I can close the review dialog", async ({ await sliceMachinePage.gotoDefaultPage(); - // We close the in app guide to display the review dialog - await sliceMachinePage.inAppGuideDialog.closeButton.click(); - await sliceMachinePage.reviewDialog.closeButton.click(); // We verify that the review dialog is not displayed anymore