slice-machine-ui 1.19.2-dev-in-app-guide.3 → 1.19.2-dev-in-app-guide.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/pages/_app.tsx CHANGED
@@ -35,6 +35,7 @@ import { ThemeProvider as ThemeUIThemeProvider, useThemeUI } from "theme-ui";
35
35
 
36
36
  import { AppLayout, AppLayoutContent } from "@components/AppLayout";
37
37
  import { InAppGuideProvider } from "@src/features/inAppGuide/InAppGuideContext";
38
+ import { InAppGuideDialog } from "@src/features/inAppGuide/InAppGuideDialog";
38
39
 
39
40
  import SliceMachineApp from "../components/App";
40
41
  import LoadingPage from "../components/LoadingPage";
@@ -151,17 +152,18 @@ function MyApp({
151
152
  );
152
153
  }}
153
154
  >
154
- <Suspense>
155
- <InAppGuideProvider>
156
- <RouteChangeProvider>
157
- <Suspense fallback={<LoadingPage />}>
158
- <ComponentLayout>
159
- <Component {...pageProps} />
160
- </ComponentLayout>
161
- </Suspense>
162
- </RouteChangeProvider>
163
- </InAppGuideProvider>
164
- </Suspense>
155
+ <InAppGuideProvider>
156
+ <RouteChangeProvider>
157
+ <Suspense fallback={<LoadingPage />}>
158
+ <ComponentLayout>
159
+ <Component {...pageProps} />
160
+ </ComponentLayout>
161
+ </Suspense>
162
+ <Suspense>
163
+ <InAppGuideDialog />
164
+ </Suspense>
165
+ </RouteChangeProvider>
166
+ </InAppGuideProvider>
165
167
  </ErrorBoundary>
166
168
  </PersistGate>
167
169
  </ConnectedRouter>
@@ -11,14 +11,15 @@ export const root = sprinkles({
11
11
  export const count = style([
12
12
  sprinkles({
13
13
  all: "unset",
14
+ display: "revert",
14
15
  color: colors.grey1,
16
+ fontFamily: "body",
15
17
  position: "absolute",
16
18
  }),
17
19
  {
18
20
  fontSize: "9px",
19
21
  fontWeight: 500,
20
22
  left: "50%",
21
- lineHeight: "32px",
22
23
  top: "50%",
23
24
  transform: "translate(-50%, -50%)",
24
25
  },
@@ -1,24 +1,17 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
 
3
- import { FlowerBackgroundIcon } from "@src/icons/FlowerBackgroundIcon";
4
-
5
3
  import { Counter } from "./Counter";
6
4
 
7
5
  type Story = StoryObj<typeof meta>;
8
6
 
9
7
  const meta = {
10
8
  component: Counter,
11
- argTypes: {
12
- backgroundIcon: { control: { disable: true } },
13
- children: { control: { disable: true } },
14
- },
15
9
  } satisfies Meta<typeof Counter>;
16
10
 
17
11
  export default meta;
18
12
 
19
13
  export const Default = {
20
14
  args: {
21
- backgroundIcon: <FlowerBackgroundIcon />,
22
- children: 1,
15
+ children: "1",
23
16
  },
24
17
  } satisfies Story;
@@ -1,17 +1,17 @@
1
- import { FC, PropsWithChildren, ReactNode } from "react";
1
+ import { FC, PropsWithChildren } from "react";
2
+
3
+ import { FlowerBackgroundIcon } from "@src/icons/FlowerBackgroundIcon";
2
4
 
3
5
  import * as styles from "./Counter.css";
4
6
 
5
- export type CounterProps = PropsWithChildren<{
6
- backgroundIcon: ReactNode;
7
- }>;
7
+ export type CounterProps = PropsWithChildren;
8
8
 
9
- export const Counter: FC<CounterProps> = (props) => {
10
- const { backgroundIcon, children } = props;
9
+ export const Counter: FC<PropsWithChildren> = (props) => {
10
+ const { children } = props;
11
11
 
12
12
  return (
13
13
  <div className={styles.root}>
14
- {backgroundIcon}
14
+ <FlowerBackgroundIcon />
15
15
  <span className={styles.count}>{children}</span>
16
16
  </div>
17
17
  );
@@ -5,12 +5,11 @@ import {
5
5
  SetStateAction,
6
6
  createContext,
7
7
  useContext,
8
+ useMemo,
8
9
  } from "react";
9
10
 
10
11
  import { usePersistedState } from "@src/hooks/usePersistedState";
11
12
 
12
- import { InAppGuideDialog } from "./InAppGuideDialog";
13
-
14
13
  type InAppGuideContextValue = {
15
14
  isInAppGuideOpen: boolean;
16
15
  setIsInAppGuideOpen: Dispatch<SetStateAction<boolean>>;
@@ -23,19 +22,18 @@ export const InAppGuideContext = createContext<
23
22
  export const InAppGuideProvider: FC<PropsWithChildren> = (props) => {
24
23
  const { children } = props;
25
24
  const [isInAppGuideOpen, setIsInAppGuideOpen] = usePersistedState(
26
- true,
27
25
  "isInAppGuideOpen",
26
+ true,
27
+ );
28
+
29
+ const memoizedValue = useMemo(
30
+ () => ({ isInAppGuideOpen, setIsInAppGuideOpen }),
31
+ [isInAppGuideOpen, setIsInAppGuideOpen],
28
32
  );
29
33
 
30
34
  return (
31
- <InAppGuideContext.Provider
32
- value={{
33
- isInAppGuideOpen,
34
- setIsInAppGuideOpen,
35
- }}
36
- >
35
+ <InAppGuideContext.Provider value={memoizedValue}>
37
36
  {children}
38
- <InAppGuideDialog />
39
37
  </InAppGuideContext.Provider>
40
38
  );
41
39
  };
@@ -43,7 +41,7 @@ export const InAppGuideProvider: FC<PropsWithChildren> = (props) => {
43
41
  export const useInAppGuide = () => {
44
42
  const context = useContext(InAppGuideContext);
45
43
  if (context === undefined) {
46
- throw new Error("useInAppGuide must be used within a InAppGuideProvider");
44
+ throw new Error("useInAppGuide must be used within an InAppGuideProvider");
47
45
  }
48
46
  return context;
49
47
  };
@@ -12,20 +12,29 @@ import {
12
12
  } from "@prismicio/editor-ui";
13
13
 
14
14
  import { Counter } from "@src/components/Counter";
15
- import { FlowerBackgroundIcon } from "@src/icons/FlowerBackgroundIcon";
16
15
  import { HelpIcon } from "@src/icons/HelpIcon";
17
16
  import { useIsEmptyProject } from "@src/hooks/useIsEmptyProject";
18
- import { useRepositoryInformation } from "@src/hooks/useRepositoryInformation";
19
17
 
20
- import { IN_APP_GUIDE_CONTENT } from "./inAppGuideContent";
18
+ import { useInAppGuideContent } from "./inAppGuideContent";
21
19
  import { useInAppGuide } from "./InAppGuideContext";
22
20
 
23
21
  export const InAppGuideDialog: FC = () => {
24
22
  const isEmptyProject = useIsEmptyProject();
25
23
  const { isInAppGuideOpen, setIsInAppGuideOpen } = useInAppGuide();
26
- const { repositoryUrl } = useRepositoryInformation();
24
+ const inAppGuideContent = useInAppGuideContent();
27
25
 
28
26
  // TODO: Use new IconButton component from editor-ui
27
+ //
28
+ // It should be used like that:
29
+ // <IconButton
30
+ // color="purple"
31
+ // radius="full"
32
+ // renderIcon={() => <HelpIcon />}
33
+ // onClick={() => {
34
+ // setIsInAppGuideOpen(!isInAppGuideOpen);
35
+ // }}
36
+ // />
37
+
29
38
  const trigger =
30
39
  isEmptyProject === false ? (
31
40
  <Box position="fixed" right={16} bottom={16}>
@@ -62,19 +71,25 @@ export const InAppGuideDialog: FC = () => {
62
71
  position="bottomRight"
63
72
  size={{
64
73
  width: 360,
65
- height: 440,
74
+ height: 456,
66
75
  }}
67
76
  >
68
- <DialogHeader title={IN_APP_GUIDE_CONTENT.title} />
77
+ <DialogHeader title={inAppGuideContent.title} />
69
78
  <DialogContent>
70
79
  <ScrollArea>
71
- {IN_APP_GUIDE_CONTENT.steps.map((content, index) => (
80
+ <Box flexDirection="column" padding={{ inline: 16, block: 8 }}>
81
+ <Text>{inAppGuideContent.description}</Text>
82
+ </Box>
83
+
84
+ <Box>
85
+ <Separator />
86
+ </Box>
87
+
88
+ {inAppGuideContent.steps.map((content, index) => (
72
89
  <Box key={index} flexDirection="column">
73
90
  <Box flexDirection="column" padding={16} gap={8}>
74
91
  <Box alignItems="center" gap={8}>
75
- <Counter backgroundIcon={<FlowerBackgroundIcon />}>
76
- {index + 1}
77
- </Counter>
92
+ <Counter>{index + 1}</Counter>
78
93
  <Text component="h3" variant="bold">
79
94
  {content.title}
80
95
  </Text>
@@ -86,11 +101,7 @@ export const InAppGuideDialog: FC = () => {
86
101
  controls
87
102
  loop
88
103
  />
89
- <Text>
90
- {typeof content.description === "function"
91
- ? content.description(repositoryUrl)
92
- : content.description}
93
- </Text>
104
+ <Text>{content.description}</Text>
94
105
  </Box>
95
106
 
96
107
  <Separator />
@@ -98,12 +109,8 @@ export const InAppGuideDialog: FC = () => {
98
109
  ))}
99
110
 
100
111
  <Box flexDirection="column" padding={16} gap={4}>
101
- <Text variant="bold">{IN_APP_GUIDE_CONTENT.successTitle}</Text>
102
- <Box flexDirection="column">
103
- {IN_APP_GUIDE_CONTENT.successSteps.map((content, index) => (
104
- <Text key={index}>• {content}</Text>
105
- ))}
106
- </Box>
112
+ <Text variant="bold">{inAppGuideContent.successTitle}</Text>
113
+ <Text>{inAppGuideContent.successDescription}</Text>
107
114
  </Box>
108
115
  </ScrollArea>
109
116
  </DialogContent>
@@ -1,60 +1,67 @@
1
- import { TextLink } from "@prismicio/editor-ui";
1
+ import { TextLink, Text } from "@prismicio/editor-ui";
2
2
 
3
- export const IN_APP_GUIDE_CONTENT = {
4
- title: "Build your first page in 5 minutes",
5
- steps: [
6
- {
7
- title: "Add slice to your page",
8
- videoUrl:
9
- "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/ADD_SLICE.mp4",
10
- description:
11
- "Try one of our templates to get started after the creation of your first page",
12
- },
13
- {
14
- title: "Code your page",
15
- videoUrl:
16
- "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/CODE_SNIP.mp4",
17
- description:
18
- "Slice Machine provides code snippets. Copy-paste them into your page component to render your page.",
19
- },
20
- {
21
- title: "Push models to the Page Builder",
22
- videoUrl:
23
- "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PUSH.mp4",
24
- description:
25
- "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.",
26
- },
27
- {
28
- title: "Create your first page",
29
- videoUrl:
30
- "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/WRITE.mp4",
31
- description: (repositoryUrl: string) => (
32
- <>
33
- In your{" "}
34
- <TextLink href={repositoryUrl} underline>
35
- Page Builder
36
- </TextLink>
37
- , create your first page. Add slices to the page to create content.
38
- Then save and publish and come back here.
39
- </>
40
- ),
41
- },
42
- {
43
- title: "Render your page",
44
- videoUrl:
45
- "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PREVIEW.mp4",
46
- description: "Run your project on localhost to see your first page.",
47
- },
48
- ],
49
- successTitle: "You have made your first page!",
50
- successSteps: [
51
- "Edit, and style your slices",
52
- "Create more pages in the Page Builder",
53
- <>
54
- Learn how to{" "}
55
- <TextLink href="TODO" underline>
56
- manage your routes
57
- </TextLink>
58
- </>,
59
- ],
60
- };
3
+ import { useRepositoryInformation } from "@src/hooks/useRepositoryInformation";
4
+
5
+ export function useInAppGuideContent() {
6
+ const { repositoryUrl } = useRepositoryInformation();
7
+
8
+ return {
9
+ title: "Build a page in 5 minutes",
10
+ description:
11
+ "Great, now that you have a page type, let's make it a live page!",
12
+ steps: [
13
+ {
14
+ title: "Add slices to your page type",
15
+ videoUrl:
16
+ "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/ADD_SLICE.mp4",
17
+ description: "Use slice templates and add them to your page type.",
18
+ },
19
+ {
20
+ title: "Code your page",
21
+ videoUrl:
22
+ "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/CODE_SNIP.mp4",
23
+ description:
24
+ "If you don't already have a page component, copy-paste the page snippets provided in your page type to create one.",
25
+ },
26
+ {
27
+ title: "Push to your Page Builder",
28
+ videoUrl:
29
+ "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PUSH.mp4",
30
+ description:
31
+ "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.",
32
+ },
33
+ {
34
+ title: "Create content",
35
+ videoUrl:
36
+ "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/WRITE.mp4",
37
+ description: (
38
+ <>
39
+ Open your{" "}
40
+ <TextLink href={repositoryUrl} underline>
41
+ Page Builder
42
+ </TextLink>
43
+ , create a page, add slices, save and publish. Then, come back here.
44
+ </>
45
+ ),
46
+ },
47
+ {
48
+ title: "Render your page",
49
+ videoUrl:
50
+ "https://res.cloudinary.com/dmtf1daqp/video/upload/v1700213517/IN-APP-GUIDE-SM/PREVIEW.mp4",
51
+ description: (
52
+ <>
53
+ To render the page, run your project in your terminal and open
54
+ localhost to the page UID.{" "}
55
+ <Text variant="italic" component="span">
56
+ Example:
57
+ </Text>{" "}
58
+ localhost:3000/my_page_uid
59
+ </>
60
+ ),
61
+ },
62
+ ],
63
+ successTitle: "Next",
64
+ successDescription:
65
+ "Create more slices and then go back to the Page Builder to build out your website.",
66
+ };
67
+ }
@@ -2,21 +2,32 @@ import { Dispatch, SetStateAction, useEffect, useState } from "react";
2
2
 
3
3
  type PersistedState<T> = [T, Dispatch<SetStateAction<T>>];
4
4
 
5
- export const SLICE_MACHINE_STORAGE_PREFIX = "slice-machine";
5
+ const SLICE_MACHINE_STORAGE_PREFIX = "slice-machine";
6
6
 
7
7
  export function usePersistedState<T>(
8
- defaultValue: T,
9
8
  key: string,
9
+ defaultValue: T,
10
10
  ): PersistedState<T> {
11
11
  const computedKey = `${SLICE_MACHINE_STORAGE_PREFIX}_${key}`;
12
+
12
13
  const [value, setValue] = useState<T>(() => {
13
- const value = localStorage.getItem(computedKey);
14
+ try {
15
+ const value = localStorage.getItem(computedKey);
16
+
17
+ return value !== null ? (JSON.parse(value) as T) : defaultValue;
18
+ } catch (error) {
19
+ console.warn(`Error reading localStorage key “${computedKey}”:`, error);
14
20
 
15
- return value !== null ? (JSON.parse(value) as T) : defaultValue;
21
+ return defaultValue;
22
+ }
16
23
  });
17
24
 
18
25
  useEffect(() => {
19
- localStorage.setItem(computedKey, JSON.stringify(value));
26
+ try {
27
+ localStorage.setItem(computedKey, JSON.stringify(value));
28
+ } catch (error) {
29
+ console.warn(`Error setting localStorage key “${computedKey}”:`, error);
30
+ }
20
31
  }, [computedKey, value]);
21
32
 
22
33
  return [value, setValue];
@@ -1,12 +1,13 @@
1
1
  import { FC } from "react";
2
2
 
3
- export const FlowerBackgroundIcon: FC = () => (
3
+ export const FlowerBackgroundIcon: FC = (props) => (
4
4
  <svg
5
5
  width="16"
6
6
  height="16"
7
7
  viewBox="0 0 16 16"
8
8
  fill="none"
9
9
  xmlns="http://www.w3.org/2000/svg"
10
+ {...props}
10
11
  >
11
12
  <path
12
13
  d="M16 8C15.9847 7.44998 15.8169 6.91443 15.5147 6.45381C15.2133 5.99404 14.7893 5.62622 14.2903 5.39293C14.4802 4.87612 14.5202 4.31673 14.4095 3.77778C14.298 3.23797 14.0375 2.74074 13.6586 2.34142C13.2584 1.96254 12.762 1.70285 12.2222 1.59046C11.6833 1.47978 11.1239 1.5198 10.6071 1.70966C10.3746 1.20988 10.0077 0.785015 9.54704 0.48361C9.08642 0.182205 8.55087 0.0136228 8 0C7.44998 0.0144742 6.91613 0.181354 6.45636 0.48361C5.99659 0.785866 5.63133 1.21073 5.4006 1.70966C4.88293 1.5198 4.32184 1.47808 3.78118 1.59046C3.24053 1.70115 2.74244 1.96169 2.34227 2.34142C1.96339 2.74159 1.70456 3.23968 1.59472 3.77863C1.48404 4.31758 1.52661 4.87697 1.71733 5.39293C1.21754 5.62622 0.791826 5.99319 0.488719 6.45296C0.185611 6.91273 0.0161771 7.44913 0 8C0.0170285 8.55087 0.185611 9.08642 0.488719 9.54704C0.791826 10.0068 1.21754 10.3746 1.71733 10.6071C1.52661 11.123 1.48404 11.6824 1.59472 12.2214C1.70541 12.7612 1.96339 13.2584 2.34142 13.6586C2.74159 14.0358 3.23882 14.2946 3.77778 14.4061C4.31673 14.5185 4.87612 14.4777 5.39293 14.2903C5.62622 14.7893 5.99319 15.2133 6.45381 15.5155C6.91358 15.8169 7.44998 15.9847 8 16C8.55087 15.9864 9.08642 15.8186 9.54704 15.5172C10.0077 15.2158 10.3746 14.7901 10.6071 14.2912C11.1213 14.4947 11.685 14.5432 12.2273 14.4308C12.7688 14.3184 13.2661 14.0502 13.6577 13.6586C14.0494 13.2669 14.3184 12.7697 14.4308 12.2273C14.5432 11.685 14.4947 11.1213 14.2903 10.6071C14.7893 10.3738 15.2133 10.0068 15.5155 9.54619C15.8169 9.08642 15.9847 8.55002 16 8Z"