Skip to content

Commit 95c1c02

Browse files
Merge branch 'develop_v3' of https://github.com/contentstack/live-preview-sdk into VE-6750/redirection-url-without-query-string-vb
2 parents 13647ea + b43682d commit 95c1c02

27 files changed

Lines changed: 2067 additions & 423 deletions

.talismanrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ fileignoreconfig:
1212
checksum: d0ef271ee5381d9feab06bda6e7e89bd0a882fee87495627bd811c1f0a5459c7
1313
- filename: src/visualBuilder/utils/__test__/getVisualBuilderRedirectionUrl.test.ts
1414
checksum: c50dc0a34b3aeb8ff50ac36af38e4ace9b6ee137522faa3d82ae2309ee285040
15+
- filename: package-lock.json
16+
checksum: fd06363871d0ee16ebfb5d9d0cc479e0922a615bb76584b80bb6933ee6c3e237
17+
- filename: src/visualBuilder/components/FieldLocationIcon.tsx
18+
checksum: d7d1330dcc55c8c1d3e154e1c55ed16132255d502046e3b23eb88b0b38833c24
19+
- filename: src/visualBuilder/components/FieldLocationAppList.tsx
20+
checksum: 5cf8f044f0e285120a0f4527bc875fc5b874d9598579e272b37f325e060f9eef
21+
- filename: src/visualBuilder/components/__test__/startEditingButton.test.tsx
22+
checksum: 3205d6199640ad160ddb62232882a019d735bb94fde87cf662629ae191e285c7
23+
- filename: src/visualBuilder/components/FieldLocationAppList.tsx
1524
version: "1.0"

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"url": "https://github.com/contentstack/live-preview-sdk.git"
8787
},
8888
"dependencies": {
89+
"@floating-ui/dom": "^1.7.2",
8990
"@preact/compat": "17.1.2",
9091
"@preact/signals": "1.2.2",
9192
"classnames": "^2.5.1",

src/__test__/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function sleep(waitTimeInMs = 100): Promise<void> {
3939
export const waitForHoverOutline = async () => {
4040
await waitFor(() => {
4141
const hoverOutline = document.querySelector(
42-
"[data-testid='visual-builder__hover-outline']"
42+
"[data-testid='visual-builder__hover-outline'][style]"
4343
);
4444
expect(hoverOutline).not.toBeNull();
4545
});
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { useState, useEffect, useMemo } from "preact/compat";
2+
import { EmptyAppIcon } from "./icons/EmptyAppIcon";
3+
import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types";
4+
import visualBuilderPostMessage from "../utils/visualBuilderPostMessage";
5+
import { visualBuilderStyles } from "../visualBuilder.style";
6+
import classNames from "classnames";
7+
import { CslpData } from "../../utils/cslpdata";
8+
9+
interface App {
10+
app_installation_uid: string;
11+
app_uid: string;
12+
contentTypeUid: string;
13+
entryUid: string;
14+
fieldDataType: string;
15+
fieldDisplayName: string;
16+
fieldPath: string;
17+
icon?: string;
18+
locale: string;
19+
manifest: {
20+
uid: string;
21+
name: string;
22+
description: string;
23+
icon: string;
24+
visibility: string;
25+
};
26+
title: string;
27+
uid: string;
28+
}
29+
30+
interface FieldLocationAppListProps {
31+
apps: App[];
32+
position: "left" | "right";
33+
toolbarRef: React.RefObject<HTMLDivElement>;
34+
domEditStack:CslpData[]
35+
setDisplayAllApps: (displayAllApps: boolean) => void;
36+
displayAllApps: boolean;
37+
}
38+
39+
const normalize = (text: string) =>
40+
text
41+
.toLowerCase()
42+
.replace(/[^a-z0-9 ]/gi, "")
43+
.trim();
44+
45+
export const FieldLocationAppList = ({
46+
apps,
47+
position,
48+
toolbarRef,
49+
domEditStack,
50+
setDisplayAllApps,
51+
}: FieldLocationAppListProps) => {
52+
const remainingApps = apps.filter((app, index) => index !== 0);
53+
const [search, setSearch] = useState("");
54+
55+
const filteredApps = useMemo(() => {
56+
if (!search.trim()) return remainingApps;
57+
58+
const normalizedSearch = normalize(search);
59+
return remainingApps.filter((app) => {
60+
return (
61+
normalize(app.title).includes(normalizedSearch)
62+
);
63+
});
64+
}, [search, remainingApps]);
65+
66+
const handleAppClick = (app: App) => {
67+
visualBuilderPostMessage?.send(
68+
VisualBuilderPostMessageEvents.FIELD_LOCATION_SELECTED_APP,
69+
{
70+
app: app,
71+
position: toolbarRef.current?.getBoundingClientRect(),
72+
DomEditStack:domEditStack
73+
}
74+
);
75+
setDisplayAllApps(false);
76+
};
77+
78+
return (
79+
<div
80+
className={classNames(
81+
visualBuilderStyles()[
82+
"visual-builder__field-location-app-list"
83+
],
84+
{
85+
[visualBuilderStyles()[
86+
"visual-builder__field-location-app-list--left"
87+
]]: position === "left",
88+
[visualBuilderStyles()[
89+
"visual-builder__field-location-app-list--right"
90+
]]: position === "right",
91+
}
92+
)}
93+
>
94+
<div
95+
className={
96+
visualBuilderStyles()[
97+
"visual-builder__field-location-app-list__search-container"
98+
]
99+
}
100+
>
101+
<svg
102+
width="14"
103+
height="14"
104+
viewBox="0 0 14 14"
105+
fill="none"
106+
xmlns="http://www.w3.org/2000/svg"
107+
className={classNames(
108+
"Search__search-icon Icon--mini",
109+
visualBuilderStyles()[
110+
"visual-builder__field-location-app-list__search-icon"
111+
]
112+
)}
113+
>
114+
<path
115+
d="M12.438 12.438L9.624 9.624M6.25 10.75a4.5 4.5 0 100-9 4.5 4.5 0 000 9z"
116+
stroke="#A9B6CB"
117+
strokeWidth="2"
118+
strokeMiterlimit="10"
119+
strokeLinecap="round"
120+
strokeLinejoin="round"
121+
></path>
122+
</svg>
123+
<input
124+
type="text"
125+
value={search}
126+
onInput={(e) =>
127+
setSearch((e.target as HTMLInputElement).value)
128+
}
129+
placeholder="Search for Apps"
130+
className={
131+
visualBuilderStyles()[
132+
"visual-builder__field-location-app-list__search-input"
133+
]
134+
}
135+
/>
136+
</div>
137+
138+
<div
139+
className={
140+
visualBuilderStyles()[
141+
"visual-builder__field-location-app-list__content"
142+
]
143+
}
144+
>
145+
{filteredApps.length === 0 && (
146+
<div
147+
className={
148+
visualBuilderStyles()[
149+
"visual-builder__field-location-app-list__no-results"
150+
]
151+
}
152+
>
153+
<span
154+
className={
155+
visualBuilderStyles()[
156+
"visual-builder__field-location-app-list__no-results-text"
157+
]
158+
}
159+
>
160+
No matching results found!
161+
</span>
162+
</div>
163+
)}
164+
{filteredApps.map((app) => (
165+
<div
166+
key={app.uid}
167+
className={
168+
visualBuilderStyles()[
169+
"visual-builder__field-location-app-list__item"
170+
]
171+
}
172+
onClick={() => handleAppClick(app)}
173+
>
174+
<div
175+
className={
176+
visualBuilderStyles()[
177+
"visual-builder__field-location-app-list__item-icon-container"
178+
]
179+
}
180+
>
181+
{app.icon ? (
182+
<img
183+
src={app.icon}
184+
alt={app.title}
185+
className={
186+
visualBuilderStyles()[
187+
"visual-builder__field-location-app-list__item-icon"
188+
]
189+
}
190+
/>
191+
) : (
192+
<EmptyAppIcon id={app.app_installation_uid} />
193+
)}
194+
</div>
195+
<span
196+
className={
197+
visualBuilderStyles()[
198+
"visual-builder__field-location-app-list__item-title"
199+
]
200+
}
201+
>
202+
{app.title}
203+
</span>
204+
</div>
205+
))}
206+
</div>
207+
</div>
208+
);
209+
};
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import classNames from "classnames";
2+
import { visualBuilderStyles } from "../visualBuilder.style";
3+
import { EmptyAppIcon } from "./icons/EmptyAppIcon";
4+
import { MoreIcon } from "./icons";
5+
import React, { useRef } from "preact/compat";
6+
import { LoadingIcon } from "./icons/loading";
7+
import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types";
8+
import visualBuilderPostMessage from "../utils/visualBuilderPostMessage";
9+
import { CslpData } from "../../utils/cslpdata";
10+
11+
export const FieldLocationIcon = ({
12+
fieldLocationData,
13+
multipleFieldToolbarButtonClasses,
14+
handleMoreIconClick,
15+
moreButtonRef,
16+
toolbarRef,
17+
domEditStack
18+
}: {
19+
fieldLocationData: any;
20+
multipleFieldToolbarButtonClasses: any;
21+
handleMoreIconClick: () => void;
22+
moreButtonRef: any;
23+
toolbarRef: any;
24+
domEditStack:CslpData[]
25+
}) => {
26+
27+
28+
29+
if (!fieldLocationData?.apps || fieldLocationData?.apps?.length === 0) {
30+
return null;
31+
}
32+
33+
const handleAppClick = (app: any) => {
34+
if(!toolbarRef.current) return
35+
visualBuilderPostMessage?.send(VisualBuilderPostMessageEvents.FIELD_LOCATION_SELECTED_APP, {
36+
app,
37+
position: toolbarRef.current?.getBoundingClientRect(),
38+
DomEditStack:domEditStack
39+
});
40+
};
41+
42+
return (
43+
<div
44+
ref={toolbarRef}
45+
className={classNames(
46+
visualBuilderStyles()[
47+
"visual-builder__field-location-icons-container"
48+
]
49+
)}
50+
>
51+
<hr
52+
className={visualBuilderStyles()["visual-builder__field-location-icons-container__divider"]}
53+
/>
54+
<button
55+
key={`${fieldLocationData.apps[0].uid}`}
56+
title={fieldLocationData.apps[0].title}
57+
className={multipleFieldToolbarButtonClasses}
58+
data-tooltip={fieldLocationData.apps[0].title}
59+
onClick={(e) => {
60+
e.preventDefault();
61+
e.stopPropagation();
62+
handleAppClick(fieldLocationData.apps[0]);
63+
}}
64+
data-testid="field-location-icon"
65+
>
66+
{fieldLocationData.apps[0].icon ? (
67+
<img
68+
src={fieldLocationData.apps[0].icon}
69+
alt={fieldLocationData.apps[0].title}
70+
className={visualBuilderStyles()["visual-builder__field-location-icons-container__app-icon"]}
71+
/>
72+
) : (
73+
<EmptyAppIcon
74+
id={fieldLocationData.apps[0].app_installation_uid}
75+
/>
76+
)}
77+
</button>
78+
79+
80+
{
81+
fieldLocationData.apps.length > 1 && (
82+
<button
83+
ref={moreButtonRef}
84+
className={multipleFieldToolbarButtonClasses}
85+
data-tooltip={"More"}
86+
onClick={handleMoreIconClick}
87+
data-testid="field-location-more-button"
88+
>
89+
<MoreIcon />
90+
</button>
91+
)
92+
}
93+
</div>
94+
);
95+
};

0 commit comments

Comments
 (0)