Skip to content

Commit d967bcc

Browse files
authored
Show variants on block model view and add breadcrumb to variant editing (#3879)
closes #3862
1 parent 5e20178 commit d967bcc

File tree

5 files changed

+209
-73
lines changed

5 files changed

+209
-73
lines changed

cypress/e2e/blocks/tests/sidebar.spec.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ describe("Blocks sidebar", () => {
1111

1212
it("can navigate blocks in the sidebar", () => {
1313
cy.contains("Test Block Do Not Delete").click();
14-
cy.location("pathname").should(
15-
"eq",
16-
"/blocks/6-d8b088cc9c-gwk3w7/7-ee94b5e98d-ss015b"
17-
);
14+
cy.location("pathname").should("eq", "/blocks/6-d8b088cc9c-gwk3w7");
1815
});
1916

2017
it("should be able to show the more options menu", () => {
Lines changed: 164 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import { useEffect, useState } from "react";
22
import { useHistory, useParams } from "react-router";
3-
import { Box, CircularProgress, Typography, Button } from "@mui/material";
3+
import {
4+
Box,
5+
CircularProgress,
6+
Typography,
7+
Button,
8+
TextField,
9+
InputAdornment,
10+
} from "@mui/material";
411
import AddRoundedIcon from "@mui/icons-material/AddRounded";
12+
import SearchRoundedIcon from "@mui/icons-material/SearchRounded";
513
import {
614
useGetContentModelItemsQuery,
715
useGetContentModelsQuery,
@@ -12,22 +20,22 @@ import { fetchModels } from "../../../shell/store/models";
1220
import { useDispatch } from "react-redux";
1321
import { fetchFields } from "../../../shell/store/fields";
1422
import { useParams as useSearchParams } from "../../../shell/hooks/useParams";
23+
import { ContentItem } from "shell/services/types";
24+
import SearchBox from "shell/components/SearchBox";
25+
import blockPlaceholder from "../../../../public/images/blockPlaceholder.png";
1526

1627
export const BlockModel = () => {
1728
const dispatch = useDispatch();
29+
const history = useHistory();
1830
const { modelZUID } = useParams<{
1931
modelZUID: string;
2032
}>();
21-
const history = useHistory();
2233
const [params] = useSearchParams();
23-
const [renderFlag, setRenderFlag] = useState(false);
2434
const [showCreateVariantDialog, setShowCreateVariantDialog] = useState(false);
25-
const { data, isFetching, error, isUninitialized } =
35+
const [search, setSearch] = useState("");
36+
const { data, isLoading, error, isUninitialized } =
2637
useGetContentModelItemsQuery({
2738
modelZUID,
28-
params: {
29-
limit: 1,
30-
},
3139
});
3240

3341
const { data: models } = useGetContentModelsQuery();
@@ -38,46 +46,110 @@ export const BlockModel = () => {
3846
dispatch(fetchFields(modelZUID));
3947
}, []);
4048

41-
useEffect(() => {
42-
if (data?.length && !isFetching && !error) {
43-
history.push(`/blocks/${modelZUID}/${data?.[0]?.meta?.ZUID}`);
44-
}
45-
46-
if (!isFetching && !isUninitialized && !data?.length) {
47-
setRenderFlag(true);
48-
}
49-
}, [data, isFetching, error, history]);
50-
5149
useEffect(() => {
5250
setShowCreateVariantDialog(!!params?.get("createVariant"));
5351
}, [params]);
5452

5553
const model = models?.find((model) => model.ZUID === modelZUID);
5654

57-
if (renderFlag) {
55+
if (isLoading || isUninitialized) {
5856
return (
59-
<>
57+
<Box
58+
display="flex"
59+
justifyContent="center"
60+
alignItems="center"
61+
height="100%"
62+
width="100%"
63+
>
64+
<CircularProgress />
65+
</Box>
66+
);
67+
}
68+
69+
return (
70+
<>
71+
<Box
72+
width="100%"
73+
display="flex"
74+
flexDirection="column"
75+
sx={{ backgroundColor: "grey.50" }}
76+
>
6077
<Box
61-
width="100%"
6278
display="flex"
63-
flexDirection="column"
64-
sx={{ backgroundColor: "grey.50" }}
79+
justifyContent="space-between"
80+
alignItems="center"
81+
px={4}
82+
pt={4}
83+
pb={1.75}
84+
sx={{
85+
borderBottom: (theme) => `2px solid ${theme.palette.border}`,
86+
backgroundColor: "background.paper",
87+
}}
6588
>
89+
<Typography variant="h3" fontWeight="700">
90+
{model?.label}
91+
</Typography>
92+
<Box display={"flex"} alignItems="center" gap={2}>
93+
<SearchBox
94+
data-cy="search-blocks-input"
95+
value={search}
96+
onChange={(event) => setSearch(event.target.value)}
97+
size="small"
98+
sx={{
99+
width: "240px",
100+
"& .MuiOutlinedInput-notchedOutline": {
101+
border: 0,
102+
},
103+
}}
104+
InputProps={{
105+
sx: {
106+
backgroundColor: "grey.50",
107+
},
108+
startAdornment: (
109+
<InputAdornment position="start">
110+
<SearchRoundedIcon fontSize="small" color="action" />
111+
</InputAdornment>
112+
),
113+
}}
114+
placeholder="Search Variants"
115+
/>
116+
<Button
117+
variant="contained"
118+
size="small"
119+
startIcon={<AddRoundedIcon />}
120+
onClick={() => setShowCreateVariantDialog(true)}
121+
data-cy="create-variant-button-header"
122+
>
123+
Create Variant
124+
</Button>
125+
</Box>
126+
</Box>
127+
{data?.length ? (
66128
<Box
129+
px={4.5}
130+
py={4}
67131
display="flex"
68-
justifyContent="space-between"
69-
px={4}
70-
pt={4}
71-
pb={1.75}
132+
gap={2}
133+
flexWrap={"wrap"}
72134
sx={{
73-
borderBottom: (theme) => `2px solid ${theme.palette.border}`,
74-
backgroundColor: "background.paper",
135+
overflowY: "scroll",
75136
}}
76137
>
77-
<Typography variant="h3" fontWeight="700">
78-
{model?.label}
79-
</Typography>
138+
{data
139+
?.filter?.((item) =>
140+
item.web.metaTitle.toLowerCase().includes(search.toLowerCase())
141+
)
142+
?.map((item) => (
143+
<BlockVariantCard
144+
key={item.meta.ZUID}
145+
item={item}
146+
onClick={() => {
147+
history.push(`/blocks/${modelZUID}/${item.meta.ZUID}`);
148+
}}
149+
/>
150+
))}
80151
</Box>
152+
) : (
81153
<Box
82154
display="flex"
83155
height="100%"
@@ -112,26 +184,72 @@ export const BlockModel = () => {
112184
/>
113185
</Box>
114186
</Box>
115-
</Box>
116-
{showCreateVariantDialog && (
117-
<CreateVariantDialog
118-
onClose={() => setShowCreateVariantDialog(false)}
119-
model={model}
120-
/>
121187
)}
122-
</>
123-
);
124-
}
188+
</Box>
189+
{showCreateVariantDialog && (
190+
<CreateVariantDialog
191+
onClose={() => setShowCreateVariantDialog(false)}
192+
model={model}
193+
/>
194+
)}
195+
</>
196+
);
197+
};
198+
199+
type BlockVariantCardProps = {
200+
item: ContentItem;
201+
onClick: () => void;
202+
};
125203

204+
const BlockVariantCard = ({ item, onClick }: BlockVariantCardProps) => {
205+
const [noImage, setNoImage] = useState(false);
126206
return (
127207
<Box
128-
display="flex"
129-
justifyContent="center"
130-
alignItems="center"
131-
height="100%"
132-
width="100%"
208+
key={item.meta.ZUID}
209+
width={265}
210+
sx={{
211+
border: (theme) => `1px solid ${theme.palette.border}`,
212+
boxSizing: "border-box",
213+
cursor: "pointer",
214+
}}
215+
onClick={onClick}
133216
>
134-
<CircularProgress />
217+
<Box px={1} pt={1} pb={1.75}>
218+
{!noImage ? (
219+
<Box
220+
component="img"
221+
src={item.data.og_image as string}
222+
minHeight={146}
223+
maxHeight={146}
224+
width="100%"
225+
sx={{ objectFit: "contain" }}
226+
onError={(e: any) => {
227+
setNoImage(true);
228+
}}
229+
/>
230+
) : (
231+
<Box
232+
minHeight={146}
233+
maxHeight={146}
234+
width="100%"
235+
component="img"
236+
sx={{ objectFit: "contain" }}
237+
src={blockPlaceholder}
238+
/>
239+
)}
240+
</Box>
241+
<Box
242+
py={2}
243+
px={1}
244+
sx={{
245+
backgroundColor: "background.paper",
246+
}}
247+
height={52}
248+
>
249+
<Typography noWrap variant="body2">
250+
{item.web.metaTitle}
251+
</Typography>
252+
</Box>
135253
</Box>
136254
);
137255
};

src/apps/content-editor/src/app/components/ContentBreadcrumbs.tsx

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ContentNavItem } from "../../../../../shell/services/types";
1717
import { MODEL_ICON } from "../../../../../shell/constants";
1818
import { Link } from "react-router-dom";
1919
import { CustomBreadcrumbs } from "../../../../../shell/components/CustomBreadcrumbs";
20+
import { useSelector } from "react-redux";
2021

2122
export const ContentBreadcrumbs = () => {
2223
const { data: nav, isLoading: isNavLoading } = useGetContentNavItemsQuery();
@@ -26,6 +27,8 @@ export const ContentBreadcrumbs = () => {
2627
}>();
2728
const history = useHistory();
2829
const location = useLocation();
30+
const models = useSelector((state: any) => state.models);
31+
const isInBlocksView = location?.pathname?.includes("/blocks/");
2932

3033
const breadcrumbData = useMemo(() => {
3134
const isInMultipageTableView = !["new", "import"].includes(
@@ -48,6 +51,17 @@ export const ContentBreadcrumbs = () => {
4851
activeItem = nav?.find((item) => item.ZUID === modelZUID);
4952
if (activeItem) {
5053
crumbs.push(activeItem);
54+
} else if (models[modelZUID]?.type === "block") {
55+
const model = models[modelZUID];
56+
activeItem = {
57+
ZUID: model.ZUID,
58+
contentModelZUID: model.ZUID,
59+
label: model.label,
60+
parentZUID: model.parentZUID,
61+
sort: model.sort,
62+
type: model.type,
63+
};
64+
crumbs.push(activeItem);
5165
} else {
5266
return [];
5367
}
@@ -74,6 +88,8 @@ export const ContentBreadcrumbs = () => {
7488
onClick: () => {
7589
if (item.type === "item") {
7690
history.push(`/content/${item.contentModelZUID}/${item.ZUID}`);
91+
} else if (item.type === "block") {
92+
history.push(`/blocks/${item.ZUID}`);
7793
} else {
7894
history.push(`/content/${item.contentModelZUID}`);
7995
}
@@ -129,27 +145,31 @@ export const ContentBreadcrumbs = () => {
129145
return (
130146
<CustomBreadcrumbs
131147
items={[
132-
{
133-
node: (
134-
<Link
135-
style={{
136-
display: "flex",
137-
}}
138-
to={`/content/${
139-
nav?.find((item) => item.label === "Homepage")?.contentModelZUID
140-
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`}
141-
>
142-
<Home color="action" fontSize="small" />
143-
</Link>
144-
),
145-
onClick: () => {
146-
history.push(
147-
`/content/${
148-
nav?.find((item) => item.label === "Homepage")?.contentModelZUID
149-
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`
150-
);
151-
},
152-
},
148+
...(!isInBlocksView
149+
? [
150+
{
151+
node: (
152+
<Link
153+
style={{ display: "flex" }}
154+
to={`/content/${
155+
nav?.find((item) => item.label === "Homepage")
156+
?.contentModelZUID
157+
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`}
158+
>
159+
<Home color="action" fontSize="small" />
160+
</Link>
161+
),
162+
onClick: () => {
163+
history.push(
164+
`/content/${
165+
nav?.find((item) => item.label === "Homepage")
166+
?.contentModelZUID
167+
}/${nav?.find((item) => item.label === "Homepage")?.ZUID}`
168+
);
169+
},
170+
},
171+
]
172+
: []),
153173
...breadcrumbData,
154174
]}
155175
/>

src/apps/content-editor/src/app/views/ItemEdit/components/ItemEditHeader/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export const ItemEditHeader = ({
141141
>
142142
<Box display="flex" justifyContent="space-between" gap={4}>
143143
<Box>
144-
{type !== "block" && <ContentBreadcrumbs />}
144+
<ContentBreadcrumbs />
145145
{isLoadingItem &&
146146
(!modelLabel || !item || !Object.keys(item?.web).length) ? (
147147
<Stack>

src/shell/services/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,8 @@ type ContentNavItemType =
446446
| "internal"
447447
| "item"
448448
| "pageset"
449-
| "redirect";
449+
| "redirect"
450+
| "block";
450451

451452
export interface ContentNavItem {
452453
ZUID: string;

0 commit comments

Comments
 (0)