slice-machine-ui 1.19.3-dev-next-release.3 → 1.19.3-dev-environments.6

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.
Files changed (80) hide show
  1. package/components/AppLayout/index.tsx +34 -10
  2. package/components/Navigation/Environment.tsx +47 -0
  3. package/components/Navigation/index.css.ts +6 -0
  4. package/components/Navigation/index.tsx +15 -2
  5. package/lib/builders/SliceBuilder/Sidebar/{AddVariationModal.tsx → VariationModal.tsx} +3 -3
  6. package/lib/builders/SliceBuilder/Sidebar/index.tsx +13 -52
  7. package/lib/builders/SliceBuilder/index.tsx +27 -31
  8. package/out/404.html +1 -1
  9. package/out/_next/static/{0Lwj5aWsM8JhkX_oWgDHj → SPaOafYlpiOTCKrKML7gk}/_buildManifest.js +1 -1
  10. package/out/_next/static/chunks/840-5b485c737b24324b.js +1 -0
  11. package/out/_next/static/chunks/904-e290f0254cc4b5df.js +1 -0
  12. package/out/_next/static/chunks/{958-c90d60818f1c468a.js → 958-be627006594efb21.js} +1 -1
  13. package/out/_next/static/chunks/pages/{_app-d42e615a839b8752.js → _app-2af35c541ee1bab0.js} +7 -7
  14. package/out/_next/static/chunks/pages/{changelog-fa36b9995c26e1d7.js → changelog-4b8369d91d82b28b.js} +1 -1
  15. package/out/_next/static/chunks/pages/changes-db6e8c6be5ae076e.js +1 -0
  16. package/out/_next/static/chunks/pages/labs-2922105ab714d32a.js +1 -0
  17. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-ca780766d02c801b.js +1 -0
  18. package/out/_next/static/chunks/pages/slices-93b7af2624c85b8e.js +1 -0
  19. package/out/_next/static/css/{412af4286eb0b283.css → 009bd7f5ac85fc6a.css} +1 -1
  20. package/out/changelog.html +1 -1
  21. package/out/changes.html +1 -1
  22. package/out/custom-types/[customTypeId].html +1 -1
  23. package/out/custom-types.html +1 -1
  24. package/out/index.html +1 -1
  25. package/out/labs.html +1 -1
  26. package/out/page-types/[pageTypeId].html +1 -1
  27. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  28. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  29. package/out/slices.html +1 -1
  30. package/package.json +4 -4
  31. package/pages/slices.tsx +1 -0
  32. package/src/apiClient.ts +4 -32
  33. package/src/components/Divider/Divider.css.ts +31 -0
  34. package/src/components/Divider/Divider.stories.tsx +27 -0
  35. package/src/components/Divider/Divider.tsx +25 -0
  36. package/src/components/Divider/index.ts +1 -0
  37. package/src/components/PageLayout/PageLayout.css.ts +8 -3
  38. package/src/components/PageLayout/PageLayout.stories.tsx +10 -1
  39. package/src/components/PageLayout/PageLayout.tsx +7 -2
  40. package/src/components/SideNav/SideNav.css.ts +3 -10
  41. package/src/components/SideNav/SideNav.stories.tsx +29 -2
  42. package/src/components/SideNav/SideNav.tsx +5 -1
  43. package/src/components/SideNav/SideNavEnvironmentSelector.css.ts +69 -0
  44. package/src/components/SideNav/SideNavEnvironmentSelector.tsx +163 -0
  45. package/src/components/SideNav/index.ts +1 -0
  46. package/src/features/customTypes/__tests__/CustomTypeBuilderPage.test.tsx +4 -3
  47. package/src/features/environments/actions/getActiveEnvironmentDomain.ts +11 -0
  48. package/src/features/environments/actions/getEnvironments.ts +12 -0
  49. package/src/features/environments/actions/setEnvironment.ts +18 -0
  50. package/src/features/environments/useActiveEnvironment.ts +38 -0
  51. package/src/features/environments/useEnvironments.ts +7 -0
  52. package/src/features/legacyState/actions/getLegacySliceMachineState.ts +53 -0
  53. package/src/features/legacyState/useLegacySliceMachineState.ts +7 -0
  54. package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +4 -3
  55. package/src/features/slices/sliceCards/SharedSliceCard.tsx +17 -44
  56. package/src/modules/environment/index.ts +2 -2
  57. package/src/modules/screenshots/sagas.ts +6 -3
  58. package/src/modules/selectedSlice/actions.ts +2 -7
  59. package/src/modules/selectedSlice/reducer.ts +1 -6
  60. package/src/modules/selectedSlice/sagas.ts +18 -10
  61. package/src/modules/slices/index.ts +1 -23
  62. package/src/modules/toaster/utils.tsx +12 -6
  63. package/src/modules/useSliceMachineActions.ts +3 -9
  64. package/test/pages/slices.test.tsx +4 -8
  65. package/test/src/modules/__fixtures__/serverState.ts +2 -0
  66. package/test/src/modules/selectedSlice/sagas.test.ts +12 -14
  67. package/test/src/modules/toaster/utils.test.ts +34 -3
  68. package/components/DeleteVariationModal/index.tsx +0 -83
  69. package/components/Forms/RenameVariationModal/RenameVariationModal.css.ts +0 -7
  70. package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +0 -117
  71. package/components/Forms/RenameVariationModal/index.ts +0 -1
  72. package/out/_next/static/chunks/840-884e41a48b92e5d3.js +0 -1
  73. package/out/_next/static/chunks/904-69cc3c4be2bff626.js +0 -1
  74. package/out/_next/static/chunks/pages/changes-e388be4457a43c3a.js +0 -1
  75. package/out/_next/static/chunks/pages/labs-ebce03a4e7a95a4e.js +0 -1
  76. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-7c811c2c79f01a25.js +0 -1
  77. package/out/_next/static/chunks/pages/slices-22db04643850c1d8.js +0 -1
  78. package/src/features/slices/sliceBuilder/actions/deleteVariation.ts +0 -86
  79. package/src/features/slices/sliceBuilder/actions/renameVariation.ts +0 -80
  80. /package/out/_next/static/{0Lwj5aWsM8JhkX_oWgDHj → SPaOafYlpiOTCKrKML7gk}/_ssgManifest.js +0 -0
package/src/apiClient.ts CHANGED
@@ -1,11 +1,6 @@
1
1
  import { SimulatorCheckResponse } from "@models/common/Simulator";
2
2
  import { SliceMachineManagerClient } from "@slicemachine/manager/client";
3
- import {
4
- type SliceSM,
5
- Slices,
6
- type VariationSM,
7
- Variations,
8
- } from "@lib/models/common/Slice";
3
+ import { SliceSM, Slices } from "@lib/models/common/Slice";
9
4
  import { CustomTypes, CustomTypeSM } from "@lib/models/common/CustomType";
10
5
 
11
6
  import { CheckAuthStatusResponse } from "@models/common/Auth";
@@ -79,15 +74,6 @@ export const saveCustomType = async (
79
74
  };
80
75
 
81
76
  /** Slice Routes * */
82
-
83
- export const readSlice = async (libraryID: string, sliceID: string) => {
84
- const { model, errors } = await managerClient.slices.readSlice({
85
- libraryID,
86
- sliceID,
87
- });
88
- return { slice: model ? Slices.toSM(model) : undefined, errors };
89
- };
90
-
91
77
  export const renameSlice = async (
92
78
  slice: SliceSM,
93
79
  libName: string,
@@ -104,21 +90,7 @@ export const deleteSlice = async (sliceId: string, libName: string) =>
104
90
  sliceID: sliceId,
105
91
  });
106
92
 
107
- export const renameSliceVariation = async (
108
- slice: ComponentUI,
109
- variation: VariationSM,
110
- ) => {
111
- return await managerClient.slices.renameSliceVariation({
112
- libraryID: slice.from,
113
- sliceID: slice.model.id,
114
- variationID: variation.id,
115
- model: Variations.fromSM(variation),
116
- });
117
- };
118
-
119
- export const deleteSliceVariation = managerClient.slices.deleteSliceVariation;
120
-
121
- export const generateSliceCustomScreenshot = async (
93
+ export const generateSliceCustomScreenshotApiClient = async (
122
94
  params: CustomScreenshotRequest,
123
95
  ): Promise<{
124
96
  url: string;
@@ -139,7 +111,7 @@ export const generateSliceCustomScreenshot = async (
139
111
  };
140
112
  };
141
113
 
142
- export const updateSlice = async (
114
+ export const updateSliceApiClient = async (
143
115
  component: ComponentUI,
144
116
  ): Promise<
145
117
  Awaited<ReturnType<(typeof managerClient)["slices"]["updateSlice"]>>
@@ -229,7 +201,7 @@ export const saveSliceMock = async (
229
201
  });
230
202
  };
231
203
 
232
- export const getChangelog = async (): Promise<PackageChangelog> => {
204
+ export const getChangelogApiClient = async (): Promise<PackageChangelog> => {
233
205
  const [
234
206
  currentVersion,
235
207
  latestNonBreakingVersion,
@@ -0,0 +1,31 @@
1
+ import { colors, sprinkles } from "@prismicio/editor-ui";
2
+ import { style, styleVariants } from "@vanilla-extract/css";
3
+
4
+ const base = style([
5
+ sprinkles({
6
+ width: "100%",
7
+ borderStyle: "none",
8
+ margin: 0,
9
+ }),
10
+ ]);
11
+
12
+ export const variants = styleVariants({
13
+ dashed: [
14
+ base,
15
+ sprinkles({
16
+ borderColor: colors.currentColor,
17
+ borderTopStyle: "dashed",
18
+ borderWidth: 1,
19
+ }),
20
+ ],
21
+ edgeFaded: [
22
+ base,
23
+ sprinkles({
24
+ height: 1,
25
+ }),
26
+ {
27
+ backgroundImage:
28
+ "linear-gradient(to right, color-mix(in srgb, currentColor 0%, transparent), currentColor, color-mix(in srgb, currentColor 0%, transparent))",
29
+ },
30
+ ],
31
+ });
@@ -0,0 +1,27 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { colors } from "@prismicio/editor-ui";
3
+
4
+ import { Divider } from "./Divider";
5
+ import { variants } from "./Divider.css";
6
+
7
+ type Story = StoryObj<typeof meta>;
8
+
9
+ const meta = {
10
+ component: Divider,
11
+ argTypes: {
12
+ variant: {
13
+ control: { type: "select" },
14
+ options: Object.keys(variants),
15
+ },
16
+ color: {
17
+ control: { type: "select" },
18
+ options: Object.keys(colors),
19
+ },
20
+ },
21
+ } satisfies Meta<typeof Divider>;
22
+
23
+ export default meta;
24
+
25
+ export const Default = {
26
+ args: {},
27
+ } satisfies Story;
@@ -0,0 +1,25 @@
1
+ import { FC } from "react";
2
+ import { colors, sprinkles } from "@prismicio/editor-ui";
3
+ import clsx from "clsx";
4
+
5
+ import * as styles from "./Divider.css";
6
+
7
+ type DividerProps = {
8
+ color?: keyof typeof colors;
9
+ variant?: "dashed" | "edgeFaded";
10
+ className?: string;
11
+ };
12
+
13
+ export const Divider: FC<DividerProps> = (props) => {
14
+ const { variant = "dashed", color = "currentColor", className } = props;
15
+
16
+ return (
17
+ <hr
18
+ className={clsx(
19
+ styles.variants[variant],
20
+ sprinkles({ color: colors[color] }),
21
+ className,
22
+ )}
23
+ />
24
+ );
25
+ };
@@ -0,0 +1 @@
1
+ export { Divider } from "./Divider";
@@ -1,5 +1,5 @@
1
1
  import { colors, sprinkles, vars } from "@prismicio/editor-ui";
2
- import { style } from "@vanilla-extract/css";
2
+ import { style, styleVariants } from "@vanilla-extract/css";
3
3
 
4
4
  const grid = sprinkles({ all: "unset", display: "grid" });
5
5
 
@@ -23,10 +23,9 @@ export const root = style([
23
23
  },
24
24
  ]);
25
25
 
26
- export const borderTop = style([
26
+ const borderTopBase = style([
27
27
  sprinkles({
28
28
  all: "unset",
29
- backgroundColor: colors.purple9,
30
29
  display: "revert",
31
30
  height: 2,
32
31
  position: "fixed",
@@ -35,6 +34,12 @@ export const borderTop = style([
35
34
  { width: "inherit" },
36
35
  ]);
37
36
 
37
+ export const borderTop = styleVariants({
38
+ prod: [borderTopBase, sprinkles({ backgroundColor: colors.purple9 })],
39
+ stage: [borderTopBase, sprinkles({ backgroundColor: colors.indigo10 })],
40
+ dev: [borderTopBase, sprinkles({ backgroundColor: colors.amber10 })],
41
+ });
42
+
38
43
  export const pane = style([
39
44
  grid,
40
45
  sprinkles({
@@ -6,12 +6,21 @@ import {
6
6
  PageLayoutHeader,
7
7
  PageLayoutPane,
8
8
  } from "./PageLayout";
9
+ import { borderTop } from "./PageLayout.css";
9
10
 
10
11
  type Story = StoryObj<typeof meta>;
11
12
 
12
13
  const meta = {
13
14
  component: PageLayout,
14
- argTypes: { children: { control: { disable: true } } },
15
+ argTypes: {
16
+ children: {
17
+ control: { disable: true },
18
+ },
19
+ activeEnvironmentKind: {
20
+ control: { type: "select" },
21
+ options: Object.keys(borderTop),
22
+ },
23
+ },
15
24
  parameters: {
16
25
  docs: { story: { height: 256, inline: false } },
17
26
  layout: "fullscreen",
@@ -2,13 +2,18 @@ import type { FC, PropsWithChildren } from "react";
2
2
 
3
3
  import * as styles from "./PageLayout.css";
4
4
 
5
- export const PageLayout: FC<PropsWithChildren> = ({
5
+ type PageLayoutProps = PropsWithChildren<{
6
+ activeEnvironmentKind?: "prod" | "stage" | "dev";
7
+ }>;
8
+
9
+ export const PageLayout: FC<PageLayoutProps> = ({
10
+ activeEnvironmentKind = "prod",
6
11
  children,
7
12
  ...otherProps
8
13
  }) => (
9
14
  <div {...otherProps} className={styles.root}>
10
15
  {children}
11
- <div className={styles.borderTop} />
16
+ <div className={styles.borderTop[activeEnvironmentKind]} />
12
17
  </div>
13
18
  );
14
19
 
@@ -101,7 +101,6 @@ export const repositoryLinkIcon = style([
101
101
  borderWidth: 1,
102
102
  cursor: "pointer",
103
103
  height: 32,
104
- marginRight: 8,
105
104
  width: 32,
106
105
  color: colors.grey12,
107
106
  backgroundColor: {
@@ -163,15 +162,9 @@ export const listTitle = style([
163
162
  },
164
163
  ]);
165
164
 
166
- export const dashedLine = style([
167
- blockWithDisplayRevert,
168
- sprinkles({
169
- borderColor: colors.grey6,
170
- borderTopStyle: "dashed",
171
- borderWidth: 1,
172
- marginTop: 8,
173
- }),
174
- ]);
165
+ export const separator = sprinkles({
166
+ marginTop: 8,
167
+ });
175
168
 
176
169
  export const link = style([
177
170
  block,
@@ -5,7 +5,6 @@ import { LightningIcon } from "@src/icons/Lightning";
5
5
 
6
6
  import {
7
7
  SideNav,
8
- SideNavLogo,
9
8
  SideNavList,
10
9
  SideNavListItem,
11
10
  SideNavLink,
@@ -15,6 +14,7 @@ import {
15
14
  SideNavRepository,
16
15
  RightElement,
17
16
  } from "./SideNav";
17
+ import { SideNavEnvironmentSelector } from "./SideNavEnvironmentSelector";
18
18
 
19
19
  type Story = StoryObj<typeof meta>;
20
20
 
@@ -33,7 +33,34 @@ export const Default = {
33
33
  args: {
34
34
  children: (
35
35
  <>
36
- <SideNavLogo />
36
+ <SideNavEnvironmentSelector
37
+ environments={[
38
+ {
39
+ name: "Production",
40
+ domain: "example-prismic-repo",
41
+ kind: "prod",
42
+ users: [{ id: "id" }],
43
+ },
44
+ {
45
+ name: "Staging",
46
+ domain: "example-prismic-repo-staging",
47
+ kind: "stage",
48
+ users: [{ id: "id" }],
49
+ },
50
+ {
51
+ name: "Development",
52
+ domain: "example-prismic-repo-development",
53
+ kind: "dev",
54
+ users: [{ id: "id" }],
55
+ },
56
+ ]}
57
+ activeEnvironment={{
58
+ name: "Production",
59
+ domain: "example-prismic-repo",
60
+ kind: "prod",
61
+ users: [{ id: "id" }],
62
+ }}
63
+ />
37
64
 
38
65
  <SideNavRepository
39
66
  repositoryName="really long repository name"
@@ -16,6 +16,8 @@ import type { UrlObject } from "node:url";
16
16
  import LogoIcon from "@src/icons/LogoIcon";
17
17
  import OpenIcon from "@src/icons/OpenIcon";
18
18
 
19
+ import { Divider } from "../Divider";
20
+
19
21
  import * as styles from "./SideNav.css";
20
22
 
21
23
  type SideNavProps = PropsWithChildren<{ style?: CSSProperties }>;
@@ -90,7 +92,9 @@ export const SideNavListTitle: FC<PropsWithChildren> = (props) => (
90
92
  <h3 {...props} className={styles.listTitle} />
91
93
  );
92
94
 
93
- export const SideNavSeparator = () => <hr className={styles.dashedLine} />;
95
+ export const SideNavSeparator = () => (
96
+ <Divider color="grey6" className={styles.separator} />
97
+ );
94
98
 
95
99
  export type SideNavLinkProps = {
96
100
  title: string;
@@ -0,0 +1,69 @@
1
+ import { colors, sprinkles } from "@prismicio/editor-ui";
2
+ import { style, styleVariants } from "@vanilla-extract/css";
3
+
4
+ export const logo = style([
5
+ sprinkles({
6
+ height: 40,
7
+ width: 40,
8
+ }),
9
+ {
10
+ display: "block",
11
+ },
12
+ ]);
13
+
14
+ const environmentDotBase = style([
15
+ sprinkles({
16
+ flexShrink: 0,
17
+ borderRadius: "50%",
18
+ }),
19
+ {
20
+ width: 10,
21
+ height: 10,
22
+ },
23
+ ]);
24
+
25
+ export const environmentDot = styleVariants({
26
+ prod: [environmentDotBase, sprinkles({ backgroundColor: colors.purple10 })],
27
+ stage: [environmentDotBase, sprinkles({ backgroundColor: colors.indigo10 })],
28
+ dev: [environmentDotBase, sprinkles({ backgroundColor: colors.amber10 })],
29
+ });
30
+
31
+ export const menuItemEnvironmentDot = sprinkles({
32
+ marginRight: 2,
33
+ marginLeft: 2,
34
+ });
35
+
36
+ export const activeEnvironmentDot = style([
37
+ sprinkles({
38
+ position: "absolute",
39
+ bottom: 0,
40
+ right: 0,
41
+ outlineWidth: 3,
42
+ outlineColor: colors.grey2,
43
+ outlineStyle: "solid",
44
+ }),
45
+ {
46
+ transform: "translate(50%, 50%)",
47
+ },
48
+ ]);
49
+
50
+ export const activeEnvironmentName = style([
51
+ sprinkles({
52
+ overflow: "hidden",
53
+ textOverflow: "ellipsis",
54
+ whiteSpace: "nowrap",
55
+ }),
56
+ {
57
+ fontWeight: 500,
58
+ lineHeight: "1.25rem",
59
+ },
60
+ ]);
61
+
62
+ export const actionRequiredLabel = style([
63
+ sprinkles({
64
+ color: colors.purple10,
65
+ }),
66
+ {
67
+ fontWeight: 500,
68
+ },
69
+ ]);
@@ -0,0 +1,163 @@
1
+ import {
2
+ Box,
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuItem,
6
+ DropdownMenuLabel,
7
+ DropdownMenuTrigger,
8
+ Icon,
9
+ IconButton,
10
+ Text,
11
+ } from "@prismicio/editor-ui";
12
+ import { Environment } from "@slicemachine/manager/client";
13
+ import type { FC } from "react";
14
+ import clsx from "clsx";
15
+
16
+ import LogoIcon from "@src/icons/LogoIcon";
17
+
18
+ import * as styles from "./SideNavEnvironmentSelector.css";
19
+
20
+ type SideNavEnvironmentSelectorProps = {
21
+ variant?: "default" | "unauthorized";
22
+ environments?: Environment[];
23
+ activeEnvironment?: Environment;
24
+ onSelect?: (environment: Environment) => void | Promise<void>;
25
+ onLogInClick?: () => void;
26
+ };
27
+
28
+ export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
29
+ props,
30
+ ) => {
31
+ const {
32
+ variant = "default",
33
+ environments = [],
34
+ activeEnvironment,
35
+ onSelect,
36
+ onLogInClick,
37
+ } = props;
38
+
39
+ const isProductionEnvironmentActive = activeEnvironment?.kind === "prod";
40
+
41
+ return (
42
+ <Box alignItems="center" gap={16}>
43
+ <Box position="relative">
44
+ <LogoIcon className={styles.logo} />
45
+ {activeEnvironment !== undefined && (
46
+ <EnvironmentDot
47
+ kind={activeEnvironment.kind}
48
+ className={styles.activeEnvironmentDot}
49
+ />
50
+ )}
51
+ </Box>
52
+ <Box flexGrow={1} flexDirection="column" overflow="hidden">
53
+ <Text component="span" variant="small" color="grey11">
54
+ Environment
55
+ </Text>
56
+ {variant === "unauthorized" ? (
57
+ <Text component="span" className={styles.actionRequiredLabel}>
58
+ Login required
59
+ </Text>
60
+ ) : activeEnvironment === undefined ? (
61
+ <Text component="span" className={styles.actionRequiredLabel}>
62
+ Select environment
63
+ </Text>
64
+ ) : (
65
+ <Text component="span" className={styles.activeEnvironmentName}>
66
+ {isProductionEnvironmentActive
67
+ ? "Production"
68
+ : activeEnvironment?.name}
69
+ </Text>
70
+ )}
71
+ </Box>
72
+ <Box flexShrink={0}>
73
+ {variant === "unauthorized" ? (
74
+ <IconButton icon="arrowForward" onClick={onLogInClick} />
75
+ ) : !activeEnvironment || environments.length > 1 ? (
76
+ <EnvironmentDropdownMenu
77
+ environments={environments}
78
+ activeEnvironment={activeEnvironment}
79
+ onSelect={onSelect}
80
+ />
81
+ ) : null}
82
+ </Box>
83
+ </Box>
84
+ );
85
+ };
86
+
87
+ type EnvironmentDropdownMenuProps = Pick<
88
+ SideNavEnvironmentSelectorProps,
89
+ "environments" | "activeEnvironment" | "onSelect"
90
+ >;
91
+
92
+ const EnvironmentDropdownMenu: FC<EnvironmentDropdownMenuProps> = (props) => {
93
+ const { environments = [], activeEnvironment, onSelect } = props;
94
+
95
+ return (
96
+ <DropdownMenu modal>
97
+ <DropdownMenuTrigger>
98
+ <IconButton icon="unfoldMore" />
99
+ </DropdownMenuTrigger>
100
+ <DropdownMenuContent align="end" minWidth={256}>
101
+ <DropdownMenuLabel>Regular Environments</DropdownMenuLabel>
102
+ {environments.map((environment) =>
103
+ environment.kind !== "dev" ? (
104
+ <EnvironmentDropdownMenuItem
105
+ key={environment.domain}
106
+ environment={environment}
107
+ onSelect={onSelect}
108
+ isActive={environment.domain === activeEnvironment?.domain}
109
+ />
110
+ ) : (
111
+ <>
112
+ <DropdownMenuLabel>Personal Environment</DropdownMenuLabel>
113
+ <EnvironmentDropdownMenuItem
114
+ key={environment.domain}
115
+ environment={environment}
116
+ onSelect={onSelect}
117
+ isActive={environment.domain === activeEnvironment?.domain}
118
+ />
119
+ </>
120
+ ),
121
+ )}
122
+ </DropdownMenuContent>
123
+ </DropdownMenu>
124
+ );
125
+ };
126
+
127
+ type EnvironmentDropdownMenuItemProps = {
128
+ environment: Environment;
129
+ isActive?: boolean;
130
+ } & Pick<SideNavEnvironmentSelectorProps, "onSelect">;
131
+
132
+ const EnvironmentDropdownMenuItem: FC<EnvironmentDropdownMenuItemProps> = (
133
+ props,
134
+ ) => {
135
+ const { environment, onSelect, isActive } = props;
136
+ const isProductionEnvironment = environment.kind === "prod";
137
+
138
+ return (
139
+ <DropdownMenuItem
140
+ startIcon={
141
+ <EnvironmentDot
142
+ kind={environment.kind}
143
+ className={styles.menuItemEnvironmentDot}
144
+ />
145
+ }
146
+ endIcon={Boolean(isActive) ? <Icon name="check" /> : undefined}
147
+ onSelect={() => void onSelect?.(environment)}
148
+ >
149
+ {isProductionEnvironment ? "Production" : environment.name}
150
+ </DropdownMenuItem>
151
+ );
152
+ };
153
+
154
+ type EnvironmentDotProps = {
155
+ kind: Environment["kind"];
156
+ className?: string;
157
+ };
158
+
159
+ const EnvironmentDot: FC<EnvironmentDotProps> = (props) => {
160
+ const { kind, className } = props;
161
+
162
+ return <div className={clsx(styles.environmentDot[kind], className)} />;
163
+ };
@@ -1 +1,2 @@
1
1
  export * from "./SideNav";
2
+ export { SideNavEnvironmentSelector } from "./SideNavEnvironmentSelector";
@@ -178,9 +178,10 @@ async function renderCustomTypesBuilderPage({
178
178
  const renderResults = render(<CustomTypesBuilderPage />, customTypeMockStore);
179
179
 
180
180
  const folder = customType.format === "page" ? "Page types" : "Custom types";
181
- expect(
182
- screen.getByTestId(`breadcrumb-${folder}-${customType.label as string}`),
183
- ).toBeVisible();
181
+ const breadcrumb = await screen.findByTestId(
182
+ `breadcrumb-${folder}-${customType.label as string}`,
183
+ );
184
+ expect(breadcrumb).toBeVisible();
184
185
 
185
186
  return renderResults;
186
187
  }
@@ -0,0 +1,11 @@
1
+ import { managerClient } from "@src/managerClient";
2
+
3
+ export async function getActiveEnvironmentDomain() {
4
+ try {
5
+ const { environment } = await managerClient.project.readEnvironment();
6
+
7
+ return { activeEnvironmentDomain: environment };
8
+ } catch (error) {
9
+ return { error };
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ import { managerClient } from "@src/managerClient";
2
+
3
+ export async function getEnvironments() {
4
+ try {
5
+ const environments =
6
+ await managerClient.prismicRepository.fetchEnvironments();
7
+
8
+ return { environments };
9
+ } catch (error) {
10
+ return { error };
11
+ }
12
+ }
@@ -0,0 +1,18 @@
1
+ import { revalidateData } from "@prismicio/editor-support/Suspense";
2
+ import { Environment } from "@slicemachine/manager/client";
3
+
4
+ import { getLegacySliceMachineState } from "@src/features/legacyState/actions/getLegacySliceMachineState";
5
+ import { managerClient } from "@src/managerClient";
6
+
7
+ import { getActiveEnvironmentDomain } from "./getActiveEnvironmentDomain";
8
+
9
+ export async function setEnvironment(
10
+ environment: Pick<Environment, "domain">,
11
+ ): Promise<void> {
12
+ await managerClient.project.updateEnvironment({
13
+ environment: environment.domain,
14
+ });
15
+
16
+ revalidateData(getActiveEnvironmentDomain, []);
17
+ revalidateData(getLegacySliceMachineState, []);
18
+ }
@@ -0,0 +1,38 @@
1
+ import { useRequest } from "@prismicio/editor-support/Suspense";
2
+
3
+ import { getActiveEnvironmentDomain } from "./actions/getActiveEnvironmentDomain";
4
+ import { useEnvironments } from "./useEnvironments";
5
+
6
+ export function useActiveEnvironment() {
7
+ const { environments, error: useEnvironmentsError } = useEnvironments();
8
+ const { activeEnvironmentDomain, error: getActiveEnvironmentError } =
9
+ useRequest(getActiveEnvironmentDomain, []);
10
+
11
+ if (useEnvironmentsError !== undefined) {
12
+ return { error: useEnvironmentsError };
13
+ }
14
+
15
+ if (getActiveEnvironmentError !== undefined) {
16
+ return { error: getActiveEnvironmentError };
17
+ }
18
+
19
+ const activeEnvironment = environments?.find((environment) => {
20
+ if (activeEnvironmentDomain === undefined) {
21
+ return environment.kind === "prod";
22
+ }
23
+
24
+ return environment.domain === activeEnvironmentDomain;
25
+ });
26
+
27
+ if (!activeEnvironment) {
28
+ return {
29
+ error: new Error(
30
+ `The active environment (${
31
+ activeEnvironmentDomain ?? "Production"
32
+ }) does not match one of the repository's environments.`,
33
+ ),
34
+ };
35
+ }
36
+
37
+ return { activeEnvironment };
38
+ }
@@ -0,0 +1,7 @@
1
+ import { useRequest } from "@prismicio/editor-support/Suspense";
2
+
3
+ import { getEnvironments } from "./actions/getEnvironments";
4
+
5
+ export function useEnvironments() {
6
+ return useRequest(getEnvironments, []);
7
+ }