slice-machine-ui 1.23.2-beta.1 → 1.23.2-beta.11
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/.depcheckrc +3 -0
- package/components/ChangesEmptyState/OfflinePage.tsx +1 -1
- package/components/ChangesItems/ChangesItems.tsx +6 -2
- package/components/CustomTypeTable/changesPage.tsx +12 -7
- package/components/DeleteSliceModal/index.tsx +37 -24
- package/components/DeleteVariationModal/index.tsx +1 -0
- package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +65 -38
- package/components/Forms/CreateSliceModal/CreateSliceModal.tsx +3 -1
- package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +7 -2
- package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +36 -21
- package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +0 -1
- package/components/LoginModal/index.tsx +40 -2
- package/components/ModalFormCard/index.tsx +0 -1
- package/components/Navigation/ChangesItem.tsx +117 -0
- package/components/Navigation/Environment.tsx +51 -4
- package/components/Navigation/index.css.ts +1 -2
- package/components/Navigation/index.test.tsx +5 -85
- package/components/Navigation/index.tsx +32 -20
- package/components/ReviewModal/ReviewForm.tsx +0 -1
- package/lib/builders/SliceBuilder/index.tsx +3 -3
- package/lib/builders/common/EditModal/Form.jsx +0 -1
- package/lib/builders/common/Zone/Card/components/NewField.tsx +0 -1
- package/lib/models/common/ModelStatus/index.ts +25 -0
- package/lib/models/common/widgets/StructuredText/index.ts +1 -1
- package/out/404.html +1 -1
- package/out/_next/static/chunks/18539003-5f78714a35fde4a1.js +97 -0
- package/out/_next/static/chunks/25-3aa4d4b24167d340.js +1 -0
- package/out/_next/static/chunks/318-19dbcae10cdb6408.js +1 -0
- package/out/_next/static/chunks/727-de03055e40815a3a.js +1 -0
- package/out/_next/static/chunks/777-8c5507e3b119b5dc.js +8 -0
- package/out/_next/static/chunks/799-0af1215eb2b3c17e.js +1 -0
- package/out/_next/static/chunks/904-40686b0426716111.js +1 -0
- package/out/_next/static/chunks/pages/_app-bca191df4ac7078e.js +434 -0
- package/out/_next/static/chunks/pages/{changelog-afd18276fe018b24.js → changelog-cb1df3a70b20f1b8.js} +1 -1
- package/out/_next/static/chunks/pages/changes-ccfd715c2bdc4f83.js +1 -0
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-3f862ad6111a1fc0.js → [customTypeId]-97ab3b90b85d645d.js} +1 -1
- package/out/_next/static/chunks/pages/{custom-types-94b8f8dbf7727412.js → custom-types-3c8e721ec6802127.js} +1 -1
- package/out/_next/static/chunks/pages/index-cf93d16e9c63b6db.js +1 -0
- package/out/_next/static/chunks/pages/{labs-ee206005948f8e87.js → labs-2adfb5127f9c4081.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-d1ffca11c42f2603.js → [pageTypeId]-84a22c5e480d3fa9.js} +1 -1
- package/out/_next/static/chunks/pages/settings-94bcd7422e8b237d.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-897ca93c114a1435.js → simulator-aaf65fbec8f5839c.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-90bb2ea76d2abf42.js +1 -0
- package/out/_next/static/chunks/pages/slices-e0ce615c38e8338d.js +1 -0
- package/out/_next/static/css/39e089aad1742a3a.css +1 -0
- package/out/_next/static/css/{37153180c3b0dd66.css → 838fd8bb5a7c32b0.css} +1 -1
- package/out/_next/static/css/ca32882a5d2d8d88.css +1 -0
- package/out/_next/static/dS_xZnzzna6Y6uwcTgf90/_buildManifest.js +1 -0
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/settings.html +1 -0
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +9 -6
- package/pages/_app.tsx +34 -32
- package/pages/changes.tsx +27 -41
- package/pages/settings.tsx +1 -0
- package/pages/slices.tsx +20 -8
- package/postcss.config.js +10 -0
- package/src/apiClient.ts +1 -22
- package/src/components/FieldSet/FieldSet.stories.tsx +20 -11
- package/src/components/RelativeTime/RelativeTime.stories.tsx +25 -0
- package/src/components/RelativeTime/RelativeTime.tsx +40 -0
- package/src/components/RelativeTime/index.ts +1 -0
- package/src/components/SideNav/SideNav.css.ts +1 -1
- package/src/components/SideNav/SideNav.tsx +9 -5
- package/src/components/SideNav/SideNavEnvironmentSelector.tsx +31 -11
- package/src/domain/__tests__/customType.test.ts +205 -19
- package/src/domain/customType.ts +132 -7
- package/src/domain/slice.ts +38 -0
- package/src/features/autoSave/AutoSaveStatusIndicator.tsx +4 -4
- package/src/features/customTypes/PageTypeOnboarding.tsx +21 -26
- package/src/features/customTypes/actions/convertCustomToPageType.ts +14 -10
- package/src/features/customTypes/actions/createCustomType.ts +69 -0
- package/src/features/customTypes/actions/deleteCustomType.ts +13 -3
- package/src/features/customTypes/actions/renameCustomType.ts +10 -4
- package/src/features/customTypes/customTypesBuilder/CustomTypeProvider.tsx +11 -10
- package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +5 -3
- package/src/features/customTypes/customTypesTable/CustomTypesTable.tsx +8 -8
- package/src/features/customTypes/customTypesTable/CustomTypesTablePage.tsx +18 -17
- package/src/features/environments/actions/setEnvironment.ts +4 -4
- package/src/features/settings/SettingsPage.tsx +47 -0
- package/src/features/settings/git/ConnectGitRepository.tsx +126 -0
- package/src/features/settings/git/ConnectGitRepositoryBlankSlate.tsx +33 -0
- package/src/features/settings/git/GitOwnerSelect.tsx +71 -0
- package/src/features/settings/git/GitProvider.ts +40 -0
- package/src/features/settings/git/GitProviderConnectButtons.tsx +63 -0
- package/src/features/settings/git/GitRepositoriesList.tsx +76 -0
- package/src/features/settings/git/GitRepositoriesSearch.tsx +69 -0
- package/src/features/settings/git/GitRepositoryConnectDialog.tsx +85 -0
- package/src/features/settings/git/GitRepositoryDisconnectDialog.tsx +60 -0
- package/src/features/settings/git/useGitIntegrationExperiment.ts +8 -0
- package/src/features/settings/git/useGitOwners.ts +12 -0
- package/src/features/settings/git/useGitRepos.ts +24 -0
- package/src/features/settings/git/useLinkedGitRepos.ts +41 -0
- package/src/features/settings/git/useWriteAPIToken.ts +23 -0
- package/src/features/slices/actions/createSlice.ts +5 -29
- package/src/features/slices/actions/deleteSlice.ts +33 -0
- package/src/features/slices/actions/renameSlice.ts +37 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +0 -1
- package/src/features/slices/sliceBuilder/SliceBuilderProvider.tsx +12 -10
- package/src/features/sync/AutoSyncProvider.tsx +252 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css +17 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.module.css.d.ts +5 -0
- package/src/features/sync/components/AutoSyncStatusIndicator.tsx +58 -0
- package/src/features/sync/fetchUnSyncChanges.ts +40 -0
- package/src/features/sync/getUnSyncChanges.ts +144 -0
- package/src/features/sync/useUnSyncChanges.ts +32 -0
- package/src/hooks/useActionQueue.tsx +177 -0
- package/src/hooks/useAuthStatus.ts +12 -0
- package/src/hooks/useUser.ts +14 -0
- package/src/icons/SettingsIcon.tsx +19 -0
- package/src/icons/SyncFailed.tsx +30 -0
- package/src/icons/Synced.tsx +19 -0
- package/src/icons/Syncing.tsx +23 -0
- package/src/modules/availableCustomTypes/index.ts +29 -135
- package/src/modules/loading/types.ts +0 -5
- package/src/modules/modal/types.ts +0 -3
- package/src/modules/slices/index.ts +27 -193
- package/src/modules/slices/selector.ts +0 -14
- package/src/modules/useSliceMachineActions.ts +70 -89
- package/src/modules/userContext/index.ts +3 -3
- package/src/redux/saga.ts +0 -4
- package/test/__testutils__/index.tsx +8 -5
- package/test/src/modules/__fixtures__/serverState.ts +1 -2
- package/test/src/modules/availableCustomTypes/index.test.ts +2 -243
- package/test/src/modules/slices.test.ts +1 -267
- package/components/Navigation/ChangesListItem.tsx +0 -92
- package/components/Navigation/ChangesRightElement.tsx +0 -33
- package/out/_next/static/chunks/18539003-5cc559566b738915.js +0 -97
- package/out/_next/static/chunks/25-37d6fa7f064be04b.js +0 -1
- package/out/_next/static/chunks/318-ce7e74f18f2d5b22.js +0 -1
- package/out/_next/static/chunks/376-7e784d6069e01654.js +0 -8
- package/out/_next/static/chunks/727-3a3bc32518778317.js +0 -1
- package/out/_next/static/chunks/731-501990ebe6a71b32.js +0 -1
- package/out/_next/static/chunks/904-9a4c1264727ae89a.js +0 -1
- package/out/_next/static/chunks/pages/_app-cd9d4c76a4abc11a.js +0 -432
- package/out/_next/static/chunks/pages/changes-2a976f9efeb719aa.js +0 -1
- package/out/_next/static/chunks/pages/index-64cfb8b0948ba8ef.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b2a9d773ce5d7739.js +0 -1
- package/out/_next/static/chunks/pages/slices-e9c70c082ca3e3f9.js +0 -1
- package/out/_next/static/css/200244e3e0463c39.css +0 -1
- package/out/_next/static/m6psnC5u1xT7Y6osWHKjp/_buildManifest.js +0 -1
- package/src/features/autoSave/useAutoSave.tsx +0 -177
- package/src/features/customTypes/__tests__/createCustomType.test.ts +0 -159
- package/src/features/customTypes/customTypesTable/createCustomType.ts +0 -107
- package/src/features/legacyState/actions/getLegacySliceMachineState.ts +0 -53
- package/src/features/legacyState/useLegacySliceMachineState.ts +0 -7
- package/src/hooks/useModelStatus.ts +0 -78
- package/src/hooks/useUnSyncChanges.ts +0 -88
- package/test/components/CreateCustomTypeModal.test.tsx +0 -73
- package/test/components/LoginModal.test.tsx +0 -67
- package/test/src/hooks/useModelStatus.test.ts +0 -115
- /package/out/_next/static/{m6psnC5u1xT7Y6osWHKjp → dS_xZnzzna6Y6uwcTgf90}/_ssgManifest.js +0 -0
- /package/src/features/{changes → sync}/actions/pushChanges.ts +0 -0
- /package/src/features/{changes → sync}/actions/trackPushChangesSuccess.ts +0 -0
|
@@ -193,11 +193,14 @@ export const WithHeaderAndBoxContent = {
|
|
|
193
193
|
<OwnerSelect />
|
|
194
194
|
</FieldSetHeader>
|
|
195
195
|
<FieldSetContent>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
<Box
|
|
197
|
+
flexDirection="column"
|
|
198
|
+
/*
|
|
199
|
+
* TODO: these `padding` values actually don't match Figma, but they
|
|
200
|
+
* are the closest allowed by the `Box` component.
|
|
201
|
+
*/
|
|
202
|
+
padding={{ block: 72, inline: 100 }}
|
|
203
|
+
>
|
|
201
204
|
<Text align="center" variant="emphasized">
|
|
202
205
|
No Results Found
|
|
203
206
|
</Text>
|
|
@@ -230,12 +233,18 @@ function OwnerSelect(props: OwnerSelectProps) {
|
|
|
230
233
|
size="large"
|
|
231
234
|
sx={{ width: "calc(50% - 8px)" }}
|
|
232
235
|
>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
{[...Array(2).keys()].map((index) => (
|
|
237
|
+
<SelectItem
|
|
238
|
+
key={index}
|
|
239
|
+
renderStartIcon={() => (
|
|
240
|
+
<GitHubIcon color={tokens.color.greyLight11} />
|
|
241
|
+
)}
|
|
242
|
+
size="large"
|
|
243
|
+
value={`owner-${index}`}
|
|
244
|
+
>
|
|
245
|
+
Owner
|
|
246
|
+
</SelectItem>
|
|
247
|
+
))}
|
|
239
248
|
</Select>
|
|
240
249
|
);
|
|
241
250
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
|
|
3
|
+
import { RelativeTime } from "./RelativeTime";
|
|
4
|
+
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
component: RelativeTime,
|
|
9
|
+
argTypes: {
|
|
10
|
+
date: { control: "date" },
|
|
11
|
+
},
|
|
12
|
+
} satisfies Meta<typeof RelativeTime>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
export const Default = {
|
|
17
|
+
args: {
|
|
18
|
+
date: new Date("1 Jan 2024"),
|
|
19
|
+
},
|
|
20
|
+
render: ({ date, ...otherArgs }) => (
|
|
21
|
+
// The `date` control converts the `date` arg into a UNIX timestamp. That's
|
|
22
|
+
// why we need to convert it back into a `Date` object.
|
|
23
|
+
<RelativeTime {...otherArgs} date={new Date(date)} />
|
|
24
|
+
),
|
|
25
|
+
} satisfies Story;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Text } from "@prismicio/editor-ui";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
|
|
4
|
+
type RelativeTimeProps = {
|
|
5
|
+
date: Date;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const RelativeTime: FC<RelativeTimeProps> = ({
|
|
9
|
+
date,
|
|
10
|
+
...otherProps
|
|
11
|
+
}) => (
|
|
12
|
+
<Text {...otherProps} color="inherit" variant="inherit">
|
|
13
|
+
{formatRelativeTime(date)}
|
|
14
|
+
</Text>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const rtf = new Intl.RelativeTimeFormat("en", {
|
|
18
|
+
numeric: "auto",
|
|
19
|
+
style: "narrow",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function formatRelativeTime(date: Date, now = new Date()): string {
|
|
23
|
+
return rtf.format(differenceInCalendarDays(date, now), "day");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const millisecondsInDay = 1_000 * 60 * 60 * 24;
|
|
27
|
+
|
|
28
|
+
// Adapted from: https://github.com/date-fns/date-fns/blob/6f44a167e71053999c10aa9462d7f0c52fec0faa/src/differenceInCalendarDays/index.ts.
|
|
29
|
+
function differenceInCalendarDays(dateLeft: Date, dateRight: Date): number {
|
|
30
|
+
const startOfDayLeft = startOfDay(dateLeft);
|
|
31
|
+
const startOfDayRight = startOfDay(dateRight);
|
|
32
|
+
return Math.round((+startOfDayLeft - +startOfDayRight) / millisecondsInDay);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Adapted from: https://github.com/date-fns/date-fns/blob/6f44a167e71053999c10aa9462d7f0c52fec0faa/src/startOfDay/index.ts.
|
|
36
|
+
function startOfDay(date: Date): Date {
|
|
37
|
+
const newDate = new Date(date);
|
|
38
|
+
newDate.setHours(0, 0, 0, 0);
|
|
39
|
+
return newDate;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RelativeTime } from "./RelativeTime";
|
|
@@ -173,10 +173,14 @@ export const UpdateInfo: FC<UpdateInfoProps> = ({
|
|
|
173
173
|
Some updates of Slice Machine are available.
|
|
174
174
|
</p>
|
|
175
175
|
|
|
176
|
-
{
|
|
177
|
-
component
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
{
|
|
177
|
+
// TODO(DT-1942): This should be a Button with a link component for
|
|
178
|
+
// accessibility
|
|
179
|
+
createElement(
|
|
180
|
+
component,
|
|
181
|
+
{ ...{ className: styles.updateInfoLink, onClick }, href },
|
|
182
|
+
"Learn more",
|
|
183
|
+
)
|
|
184
|
+
}
|
|
181
185
|
</div>
|
|
182
186
|
);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
Icon,
|
|
9
9
|
IconButton,
|
|
10
10
|
InvisibleButton,
|
|
11
|
+
ProgressCircle,
|
|
11
12
|
Text,
|
|
12
13
|
} from "@prismicio/editor-ui";
|
|
13
14
|
import { Environment } from "@slicemachine/manager/client";
|
|
@@ -21,22 +22,26 @@ import * as styles from "./SideNavEnvironmentSelector.css";
|
|
|
21
22
|
import { LoginIcon } from "@src/icons/LoginIcon";
|
|
22
23
|
|
|
23
24
|
type SideNavEnvironmentSelectorProps = {
|
|
24
|
-
variant?: "default" | "offline" | "unauthorized" | "unauthenticated";
|
|
25
|
-
environments?: Environment[];
|
|
26
25
|
activeEnvironment?: Environment;
|
|
27
|
-
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
environments?: Environment[];
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
variant?: "default" | "offline" | "unauthorized" | "unauthenticated";
|
|
28
30
|
onLogInClick?: () => void;
|
|
31
|
+
onSelect?: (environment: Environment) => void | Promise<void>;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
|
|
32
35
|
props,
|
|
33
36
|
) => {
|
|
34
37
|
const {
|
|
35
|
-
variant = "default",
|
|
36
|
-
environments = [],
|
|
37
38
|
activeEnvironment,
|
|
38
|
-
|
|
39
|
+
disabled = false,
|
|
40
|
+
environments = [],
|
|
41
|
+
loading = false,
|
|
42
|
+
variant = "default",
|
|
39
43
|
onLogInClick,
|
|
44
|
+
onSelect,
|
|
40
45
|
} = props;
|
|
41
46
|
|
|
42
47
|
const isProductionEnvironmentActive = activeEnvironment?.kind === "prod";
|
|
@@ -60,7 +65,7 @@ export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
|
|
|
60
65
|
overflow="hidden"
|
|
61
66
|
alignItems="flex-start"
|
|
62
67
|
>
|
|
63
|
-
{variant === "default"
|
|
68
|
+
{variant === "default" ? (
|
|
64
69
|
<Text component="span" variant="small" color="grey11">
|
|
65
70
|
Environment
|
|
66
71
|
</Text>
|
|
@@ -70,6 +75,12 @@ export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
|
|
|
70
75
|
<InvisibleButton buttonText="Login required" onClick={onLogInClick} />
|
|
71
76
|
) : undefined}
|
|
72
77
|
|
|
78
|
+
{variant === "offline" ? (
|
|
79
|
+
<Text component="span" variant="bold">
|
|
80
|
+
Offline
|
|
81
|
+
</Text>
|
|
82
|
+
) : undefined}
|
|
83
|
+
|
|
73
84
|
{variant === "default" ? (
|
|
74
85
|
<Text
|
|
75
86
|
component="span"
|
|
@@ -93,8 +104,10 @@ export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
|
|
|
93
104
|
|
|
94
105
|
{environments.length > 1 ? (
|
|
95
106
|
<EnvironmentDropdownMenu
|
|
96
|
-
environments={environments}
|
|
97
107
|
activeEnvironment={activeEnvironment}
|
|
108
|
+
disabled={disabled}
|
|
109
|
+
environments={environments}
|
|
110
|
+
loading={loading}
|
|
98
111
|
onSelect={onSelect}
|
|
99
112
|
/>
|
|
100
113
|
) : undefined}
|
|
@@ -107,11 +120,14 @@ type EnvironmentDropdownMenuProps = Pick<
|
|
|
107
120
|
SideNavEnvironmentSelectorProps,
|
|
108
121
|
"activeEnvironment" | "onSelect"
|
|
109
122
|
> & {
|
|
123
|
+
disabled: boolean;
|
|
110
124
|
environments: Environment[];
|
|
125
|
+
loading: boolean;
|
|
111
126
|
};
|
|
112
127
|
|
|
113
128
|
const EnvironmentDropdownMenu: FC<EnvironmentDropdownMenuProps> = (props) => {
|
|
114
|
-
const { environments,
|
|
129
|
+
const { activeEnvironment, disabled, environments, loading, onSelect } =
|
|
130
|
+
props;
|
|
115
131
|
|
|
116
132
|
const nonPersonalEnvironments = environments.filter(
|
|
117
133
|
(environment) => environment.kind !== "dev",
|
|
@@ -122,8 +138,12 @@ const EnvironmentDropdownMenu: FC<EnvironmentDropdownMenuProps> = (props) => {
|
|
|
122
138
|
|
|
123
139
|
return (
|
|
124
140
|
<DropdownMenu modal>
|
|
125
|
-
<DropdownMenuTrigger disabled={
|
|
126
|
-
<IconButton
|
|
141
|
+
<DropdownMenuTrigger disabled={disabled}>
|
|
142
|
+
<IconButton
|
|
143
|
+
icon={loading ? <ProgressCircle /> : "unfoldMore"}
|
|
144
|
+
hiddenLabel="Select environment"
|
|
145
|
+
disabled={disabled}
|
|
146
|
+
/>
|
|
127
147
|
</DropdownMenuTrigger>
|
|
128
148
|
<DropdownMenuContent align="end" minWidth={256}>
|
|
129
149
|
{personalEnvironment ? (
|
|
@@ -393,32 +393,39 @@ describe("CustomTypeModel test suite", () => {
|
|
|
393
393
|
|
|
394
394
|
it("addField should return the given custom type with the field added to the section", () => {
|
|
395
395
|
expect(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
config: {
|
|
402
|
-
label: "NewField",
|
|
403
|
-
},
|
|
404
|
-
type: "UID",
|
|
405
|
-
},
|
|
406
|
-
}),
|
|
407
|
-
).toEqual({
|
|
408
|
-
...mockCustomType,
|
|
409
|
-
json: {
|
|
410
|
-
...mockCustomType.json,
|
|
411
|
-
mainSection: {
|
|
412
|
-
...mainSection,
|
|
396
|
+
JSON.stringify(
|
|
397
|
+
CustomTypeModel.addField({
|
|
398
|
+
customType: mockCustomType,
|
|
399
|
+
sectionId: "mainSection",
|
|
400
|
+
newFieldId: "newField",
|
|
413
401
|
newField: {
|
|
414
402
|
config: {
|
|
415
403
|
label: "NewField",
|
|
416
404
|
},
|
|
417
405
|
type: "UID",
|
|
418
406
|
},
|
|
407
|
+
}),
|
|
408
|
+
),
|
|
409
|
+
).toEqual(
|
|
410
|
+
JSON.stringify({
|
|
411
|
+
...mockCustomType,
|
|
412
|
+
json: {
|
|
413
|
+
...mockCustomType.json,
|
|
414
|
+
mainSection: {
|
|
415
|
+
uid: mainSection.uid,
|
|
416
|
+
booleanField: mainSection.booleanField,
|
|
417
|
+
groupField: mainSection.groupField,
|
|
418
|
+
newField: {
|
|
419
|
+
config: {
|
|
420
|
+
label: "NewField",
|
|
421
|
+
},
|
|
422
|
+
type: "UID",
|
|
423
|
+
},
|
|
424
|
+
slices: mainSection.slices,
|
|
425
|
+
},
|
|
419
426
|
},
|
|
420
|
-
},
|
|
421
|
-
|
|
427
|
+
}),
|
|
428
|
+
);
|
|
422
429
|
});
|
|
423
430
|
|
|
424
431
|
it("deleteField should return the given custom type without the field", () => {
|
|
@@ -782,4 +789,183 @@ describe("CustomTypeModel test suite", () => {
|
|
|
782
789
|
},
|
|
783
790
|
});
|
|
784
791
|
});
|
|
792
|
+
|
|
793
|
+
it("should create a custom type with repeatable true", () => {
|
|
794
|
+
expect(
|
|
795
|
+
CustomTypeModel.create({
|
|
796
|
+
id: "id",
|
|
797
|
+
label: "lama",
|
|
798
|
+
repeatable: true,
|
|
799
|
+
format: "custom",
|
|
800
|
+
}),
|
|
801
|
+
).toMatchInlineSnapshot(`
|
|
802
|
+
{
|
|
803
|
+
"format": "custom",
|
|
804
|
+
"id": "id",
|
|
805
|
+
"json": {
|
|
806
|
+
"Main": {
|
|
807
|
+
"uid": {
|
|
808
|
+
"config": {
|
|
809
|
+
"label": "UID",
|
|
810
|
+
},
|
|
811
|
+
"type": "UID",
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
"label": "lama",
|
|
816
|
+
"repeatable": true,
|
|
817
|
+
"status": true,
|
|
818
|
+
}
|
|
819
|
+
`);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it("should create a custom type with repeatable false", () => {
|
|
823
|
+
const result = CustomTypeModel.create({
|
|
824
|
+
id: "id",
|
|
825
|
+
label: "lama",
|
|
826
|
+
repeatable: false,
|
|
827
|
+
format: "custom",
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
expect(result.id).toBe("id");
|
|
831
|
+
expect(result.label).toBe("lama");
|
|
832
|
+
expect(result.format).toBe("custom");
|
|
833
|
+
expect(result.json).toHaveProperty("Main");
|
|
834
|
+
expect(result).toMatchInlineSnapshot(`
|
|
835
|
+
{
|
|
836
|
+
"format": "custom",
|
|
837
|
+
"id": "id",
|
|
838
|
+
"json": {
|
|
839
|
+
"Main": {},
|
|
840
|
+
},
|
|
841
|
+
"label": "lama",
|
|
842
|
+
"repeatable": false,
|
|
843
|
+
"status": true,
|
|
844
|
+
}
|
|
845
|
+
`);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it("should create a page type with a slice zone", () => {
|
|
849
|
+
const result = CustomTypeModel.create({
|
|
850
|
+
id: "🥪",
|
|
851
|
+
label: "label",
|
|
852
|
+
repeatable: true,
|
|
853
|
+
format: "page",
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
expect(result.format).toBe("page");
|
|
857
|
+
expect(result.json).toHaveProperty("Main");
|
|
858
|
+
expect(result.json.Main).toHaveProperty("slices");
|
|
859
|
+
expect(result.json).toHaveProperty("SEO & Metadata");
|
|
860
|
+
expect(result).toMatchInlineSnapshot(`
|
|
861
|
+
{
|
|
862
|
+
"format": "page",
|
|
863
|
+
"id": "🥪",
|
|
864
|
+
"json": {
|
|
865
|
+
"Main": {
|
|
866
|
+
"slices": {
|
|
867
|
+
"config": {
|
|
868
|
+
"choices": {},
|
|
869
|
+
},
|
|
870
|
+
"fieldset": "Slice Zone",
|
|
871
|
+
"type": "Slices",
|
|
872
|
+
},
|
|
873
|
+
"uid": {
|
|
874
|
+
"config": {
|
|
875
|
+
"label": "UID",
|
|
876
|
+
},
|
|
877
|
+
"type": "UID",
|
|
878
|
+
},
|
|
879
|
+
},
|
|
880
|
+
"SEO & Metadata": {
|
|
881
|
+
"meta_description": {
|
|
882
|
+
"config": {
|
|
883
|
+
"label": "Meta Description",
|
|
884
|
+
"placeholder": "A brief summary of the page",
|
|
885
|
+
},
|
|
886
|
+
"type": "Text",
|
|
887
|
+
},
|
|
888
|
+
"meta_image": {
|
|
889
|
+
"config": {
|
|
890
|
+
"constraint": {
|
|
891
|
+
"height": 1260,
|
|
892
|
+
"width": 2400,
|
|
893
|
+
},
|
|
894
|
+
"label": "Meta Image",
|
|
895
|
+
"thumbnails": [],
|
|
896
|
+
},
|
|
897
|
+
"type": "Image",
|
|
898
|
+
},
|
|
899
|
+
"meta_title": {
|
|
900
|
+
"config": {
|
|
901
|
+
"label": "Meta Title",
|
|
902
|
+
"placeholder": "A title of the page used for social media and search engines",
|
|
903
|
+
},
|
|
904
|
+
"type": "Text",
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
"label": "label",
|
|
909
|
+
"repeatable": true,
|
|
910
|
+
"status": true,
|
|
911
|
+
}
|
|
912
|
+
`);
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
it("when non repeatable page type is should contain Main with a slice-zone, no uid, and a SEO tab", () => {
|
|
917
|
+
const result = CustomTypeModel.create({
|
|
918
|
+
id: "foo",
|
|
919
|
+
label: "bar",
|
|
920
|
+
repeatable: false,
|
|
921
|
+
format: "page",
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
expect(result).toMatchInlineSnapshot(`
|
|
925
|
+
{
|
|
926
|
+
"format": "page",
|
|
927
|
+
"id": "foo",
|
|
928
|
+
"json": {
|
|
929
|
+
"Main": {
|
|
930
|
+
"slices": {
|
|
931
|
+
"config": {
|
|
932
|
+
"choices": {},
|
|
933
|
+
},
|
|
934
|
+
"fieldset": "Slice Zone",
|
|
935
|
+
"type": "Slices",
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
"SEO & Metadata": {
|
|
939
|
+
"meta_description": {
|
|
940
|
+
"config": {
|
|
941
|
+
"label": "Meta Description",
|
|
942
|
+
"placeholder": "A brief summary of the page",
|
|
943
|
+
},
|
|
944
|
+
"type": "Text",
|
|
945
|
+
},
|
|
946
|
+
"meta_image": {
|
|
947
|
+
"config": {
|
|
948
|
+
"constraint": {
|
|
949
|
+
"height": 1260,
|
|
950
|
+
"width": 2400,
|
|
951
|
+
},
|
|
952
|
+
"label": "Meta Image",
|
|
953
|
+
"thumbnails": [],
|
|
954
|
+
},
|
|
955
|
+
"type": "Image",
|
|
956
|
+
},
|
|
957
|
+
"meta_title": {
|
|
958
|
+
"config": {
|
|
959
|
+
"label": "Meta Title",
|
|
960
|
+
"placeholder": "A title of the page used for social media and search engines",
|
|
961
|
+
},
|
|
962
|
+
"type": "Text",
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
"label": "bar",
|
|
967
|
+
"repeatable": false,
|
|
968
|
+
"status": true,
|
|
969
|
+
}
|
|
970
|
+
`);
|
|
785
971
|
});
|
package/src/domain/customType.ts
CHANGED
|
@@ -104,8 +104,15 @@ type GetGroupFieldArgs = {
|
|
|
104
104
|
groupFieldId: string;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
type CreateArgs = {
|
|
108
|
+
format: CustomTypeFormat;
|
|
109
|
+
id: string;
|
|
110
|
+
label: string;
|
|
111
|
+
repeatable: boolean;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export function getFormat(customType: CustomType): CustomTypeFormat {
|
|
115
|
+
return customType.format ?? "custom";
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
export function getSectionEntries(
|
|
@@ -363,15 +370,32 @@ export function renameSection(
|
|
|
363
370
|
|
|
364
371
|
export function addField(args: AddFieldArgs): CustomType {
|
|
365
372
|
const { customType, sectionId, newField, newFieldId } = args;
|
|
373
|
+
const sectionJson = customType.json[sectionId];
|
|
374
|
+
const maybeSliceZoneEntry = getSectionSliceZoneEntry(customType, sectionId);
|
|
375
|
+
|
|
376
|
+
// Separate the fields into slices and non-slices
|
|
377
|
+
const sectionFields = Object.fromEntries(
|
|
378
|
+
Object.entries(sectionJson).filter(
|
|
379
|
+
([_, value]) => value.type !== SlicesFieldType,
|
|
380
|
+
),
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const updatedSection = {
|
|
384
|
+
...sectionFields,
|
|
385
|
+
// Add the new field to the section
|
|
386
|
+
[newFieldId]: newField,
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// Merge the SlicesFieldType field back into the section, if it exists
|
|
390
|
+
if (maybeSliceZoneEntry !== undefined) {
|
|
391
|
+
const [sliceZoneKey, sliceZoneField] = maybeSliceZoneEntry;
|
|
392
|
+
updatedSection[sliceZoneKey] = sliceZoneField;
|
|
393
|
+
}
|
|
366
394
|
|
|
367
395
|
const newCustomType = updateSection({
|
|
368
396
|
customType,
|
|
369
397
|
sectionId,
|
|
370
|
-
updatedSection
|
|
371
|
-
...customType.json[sectionId],
|
|
372
|
-
// Add the new field to the section
|
|
373
|
-
[newFieldId]: newField,
|
|
374
|
-
},
|
|
398
|
+
updatedSection,
|
|
375
399
|
});
|
|
376
400
|
|
|
377
401
|
return newCustomType;
|
|
@@ -633,3 +657,104 @@ export function updateGroupFields(
|
|
|
633
657
|
},
|
|
634
658
|
};
|
|
635
659
|
}
|
|
660
|
+
|
|
661
|
+
export function create(args: CreateArgs) {
|
|
662
|
+
const { id, label, repeatable, format } = args;
|
|
663
|
+
const mainTab = makeMainTab(repeatable, format);
|
|
664
|
+
|
|
665
|
+
return {
|
|
666
|
+
format,
|
|
667
|
+
id,
|
|
668
|
+
json: mainTab,
|
|
669
|
+
label,
|
|
670
|
+
repeatable,
|
|
671
|
+
status: true,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function makeMainTab(
|
|
676
|
+
repeatable: boolean,
|
|
677
|
+
format: CustomTypeFormat,
|
|
678
|
+
): CustomType["json"] {
|
|
679
|
+
if (repeatable === false && format === "page") {
|
|
680
|
+
return { ...DEFAULT_MAIN_WITH_SLICE_ZONE, ...DEFAULT_SEO_TAB };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (repeatable === false) {
|
|
684
|
+
return DEFAULT_MAIN;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (format === "page") {
|
|
688
|
+
return {
|
|
689
|
+
...DEFAULT_MAIN_WITH_UID_AND_SLICE_ZONE,
|
|
690
|
+
...DEFAULT_SEO_TAB,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return DEFAULT_MAIN_WITH_UID;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const DEFAULT_MAIN: CustomType["json"] = {
|
|
698
|
+
Main: {},
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
const DEFAULT_MAIN_WITH_SLICE_ZONE: CustomType["json"] = {
|
|
702
|
+
Main: {
|
|
703
|
+
slices: {
|
|
704
|
+
config: {
|
|
705
|
+
choices: {},
|
|
706
|
+
},
|
|
707
|
+
fieldset: "Slice Zone",
|
|
708
|
+
type: "Slices",
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const DEFAULT_MAIN_WITH_UID: CustomType["json"] = {
|
|
714
|
+
Main: {
|
|
715
|
+
uid: {
|
|
716
|
+
config: {
|
|
717
|
+
label: "UID",
|
|
718
|
+
},
|
|
719
|
+
type: "UID",
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
const DEFAULT_MAIN_WITH_UID_AND_SLICE_ZONE: CustomType["json"] = {
|
|
725
|
+
Main: {
|
|
726
|
+
...DEFAULT_MAIN_WITH_UID.Main,
|
|
727
|
+
...DEFAULT_MAIN_WITH_SLICE_ZONE.Main,
|
|
728
|
+
},
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
const DEFAULT_SEO_TAB: CustomType["json"] = {
|
|
732
|
+
"SEO & Metadata": {
|
|
733
|
+
meta_description: {
|
|
734
|
+
config: {
|
|
735
|
+
label: "Meta Description",
|
|
736
|
+
placeholder: "A brief summary of the page",
|
|
737
|
+
},
|
|
738
|
+
type: "Text",
|
|
739
|
+
},
|
|
740
|
+
meta_image: {
|
|
741
|
+
config: {
|
|
742
|
+
constraint: {
|
|
743
|
+
height: 1260,
|
|
744
|
+
width: 2400,
|
|
745
|
+
},
|
|
746
|
+
label: "Meta Image",
|
|
747
|
+
thumbnails: [],
|
|
748
|
+
},
|
|
749
|
+
type: "Image",
|
|
750
|
+
},
|
|
751
|
+
meta_title: {
|
|
752
|
+
config: {
|
|
753
|
+
label: "Meta Title",
|
|
754
|
+
placeholder:
|
|
755
|
+
"A title of the page used for social media and search engines",
|
|
756
|
+
},
|
|
757
|
+
type: "Text",
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
};
|