Skip to content

Commit 78c9f1b

Browse files
authored
feat(UI-1433): create onboarding guided tour (#1093)
1 parent 35e6dd2 commit 78c9f1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1192
-90
lines changed

e2e/pages/dashboard.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ export class DashboardPage {
1818
await this.page.getByRole("tab", { name: "PROGRAM.PY" }).isVisible();
1919
await this.page.getByText('print("Hello World!")').isVisible();
2020
await this.page.waitForLoadState("domcontentloaded");
21+
22+
try {
23+
await this.page.getByRole("button", { name: "Skip the tour", exact: true }).click({ timeout: 2000 });
24+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
25+
} catch (error) {
26+
// eslint-disable-next-line no-console
27+
console.log("Skip the tour button not found, continuing...");
28+
}
2129
}
2230

2331
async createProjectFromTemplate(projectName: string) {
@@ -29,5 +37,13 @@ export class DashboardPage {
2937
await this.page.getByRole("button", { name: "Create Project From Template: HTTP" }).click();
3038
await this.page.getByPlaceholder("Enter project name").fill(projectName);
3139
await this.page.getByRole("button", { name: "Create", exact: true }).click();
40+
41+
try {
42+
await this.page.getByRole("button", { name: "Skip the tour", exact: true }).click({ timeout: 2000 });
43+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
44+
} catch (error) {
45+
// eslint-disable-next-line no-console
46+
console.log("Skip the tour button not found, continuing...");
47+
}
3248
}
3349
}

e2e/project/webhookSessionTriggered.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,18 @@ async function setupProjectAndTriggerSession({ dashboardPage, page, request }: S
7878
await page.getByRole("option", { name: "Samples" }).click();
7979
await page.locator("body").click({ position: { x: 0, y: 0 } });
8080
await page.getByRole("button", { name: "Create Project From Template: HTTP" }).scrollIntoViewIfNeeded();
81-
await page.getByRole("button", { name: "Create Project From Template: HTTP" }).click();
81+
await page.getByRole("button", { name: "Create Project From Template: HTTP" }).click({ timeout: 2000 });
8282

8383
await page.getByPlaceholder("Enter project name").fill(projectName);
8484
await page.getByRole("button", { name: "Create", exact: true }).click();
85+
86+
try {
87+
await page.getByRole("button", { name: "Skip the tour", exact: true }).click({ timeout: 2000 });
88+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
89+
} catch (error) {
90+
// eslint-disable-next-line no-console
91+
console.log("Skip the tour button not found, continuing...");
92+
}
8593
// eslint-disable-next-line @typescript-eslint/no-unused-vars
8694
} catch (error) {
8795
await dashboardPage.createProjectFromTemplate(projectName);

src/app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export const App = () => {
159159
path=":eventId"
160160
/>
161161
</Route>
162-
<Route element={<Navigate replace to="code" />} index />
162+
<Route element={<Navigate replace state={location.state} to="code" />} index />
163163

164164
<Route element={<Connections />} path="connections">
165165
<Route element={<ConnectionsTable />} index />

src/assets/index.css

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,51 @@ ul li::before {
186186
.swiper-prev,
187187
.swiper-next {
188188
@apply relative;
189+
}
190+
191+
[data-tour-highlight="true"] {
192+
position: relative;
193+
z-index: 50;
194+
box-shadow: 0 0 0 4px rgba(188, 248, 112, 1);
195+
animation: pulse-highlight 3s infinite;
196+
border:2px solid rgba(188, 248, 112, 1) !important;
197+
}
198+
199+
@keyframes pulse-highlight {
200+
0% {
201+
box-shadow: 0 0 0 0 rgba(188, 248, 112, 0.7);
202+
}
203+
70% {
204+
box-shadow: 0 0 0 6px rgba(188, 248, 112, 0);
205+
}
206+
100% {
207+
box-shadow: 0 0 0 0 rgba(188, 248, 112, 0);
208+
}
209+
}
210+
211+
.dashed-arrow {
212+
--c:red; /* color */
213+
--r:10px; /* circle size */
214+
--s:10px; /* space bettwen circles */
215+
216+
width:100px;
217+
height:100px;
218+
display:inline-block;
219+
margin:20px;
220+
--g:radial-gradient(circle closest-side, var(--c) 85%,transparent);
221+
background:
222+
var(--g) calc(var(--s)/-2) 0/calc(var(--r) + var(--s)) var(--r) repeat-x,
223+
var(--g) 0 calc(var(--s)/-2)/var(--r) calc(var(--r) + var(--s)) repeat-y;
224+
}
225+
226+
.dashed-arrow::after {
227+
content:"";
228+
position:absolute;
229+
top:calc(var(--r)/2);
230+
left:100%;
231+
width:20px;
232+
height:20px;
233+
transform:translateY(-50%);
234+
background:var(--c);
235+
clip-path:polygon(0 0, 100% 50%,0 100%);
189236
}

src/components/atoms/buttons/button.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const Button = ({
1313
disabled,
1414
form,
1515
href,
16+
id,
1617
onClick,
1718
onKeyPressed,
1819
style,
@@ -51,6 +52,7 @@ export const Button = ({
5152
className={buttonClass}
5253
disabled={disabled}
5354
form={form}
55+
id={id}
5456
onClick={onClick}
5557
onKeyDown={onKeyDown}
5658
onMouseEnter={onMouseEnter}
@@ -63,7 +65,15 @@ export const Button = ({
6365
{children}
6466
</button>
6567
) : (
66-
<Link ariaLabel={ariaLabel} className={buttonClass} disabled={disabled} target={target} title={title} to={href}>
68+
<Link
69+
ariaLabel={ariaLabel}
70+
className={buttonClass}
71+
disabled={disabled}
72+
id={id}
73+
target={target}
74+
title={title}
75+
to={href}
76+
>
6777
{children}
6878
</Link>
6979
);

src/components/atoms/buttons/iconButton.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const IconButton = ({
1919
title,
2020
type = "button",
2121
variant,
22+
id,
2223
}: IconButtonProps) => {
2324
const iconButtonClass = cn(
2425
"flex shrink-0 items-center justify-center rounded-full p-2 outline-0 transition duration-300 hover:bg-gray-850",
@@ -49,6 +50,7 @@ export const IconButton = ({
4950
title={title}
5051
type={type}
5152
{...linkHref}
53+
id={id}
5254
>
5355
{children}
5456
</Component>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from "react";
2+
3+
import { cn } from "@src/utilities";
4+
5+
export const DashedArrow = ({ style, className }: { className: string; style: React.CSSProperties }) => {
6+
const arrowClass = cn("dashed-arrow", className);
7+
return <div className={arrowClass} style={style} />;
8+
};

src/components/atoms/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ export { Toggle } from "@components/atoms/toggle";
2323
export { Typography } from "@components/atoms/typography";
2424
export { SuccessMessage } from "@components/atoms/successMessage";
2525
export { Tooltip } from "@components/atoms/tooltip";
26+
export { DashedArrow } from "@components/atoms/dashedArrow";

src/components/atoms/link.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Link as LinkReact } from "react-router-dom";
55
import { LinkProps } from "@interfaces/components";
66
import { cn } from "@utilities";
77

8-
export const Link = ({ ariaLabel, children, className, disabled, target, title, to }: LinkProps) => {
8+
export const Link = ({ ariaLabel, children, className, disabled, target, title, to, id }: LinkProps) => {
99
const linkClass = cn(
1010
{
1111
"pointer-events-none cursor-not-allowed select-none": disabled,
@@ -14,7 +14,7 @@ export const Link = ({ ariaLabel, children, className, disabled, target, title,
1414
);
1515

1616
return (
17-
<LinkReact aria-label={ariaLabel} className={linkClass} target={target} title={title} to={to}>
17+
<LinkReact aria-label={ariaLabel} className={linkClass} id={id} target={target} title={title} to={to}>
1818
{children}
1919
</LinkReact>
2020
);

src/components/molecules/popover/popoverContentBase.tsx

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { forwardRef } from "react";
33
import { FloatingFocusManager, FloatingPortal } from "@floating-ui/react";
44

55
import { PopoverContentBaseProps } from "@src/interfaces/components";
6+
import { cn } from "@src/utilities";
67

78
import { useMergeRefsCustom } from "@components/molecules/popover/utilities";
89

@@ -11,39 +12,20 @@ export const PopoverContentBase = forwardRef<HTMLDivElement, PopoverContentBaseP
1112
propRef
1213
) {
1314
const ref = useMergeRefsCustom(context.refs.setFloating, propRef);
14-
15-
const handleOverlayKeyDown = (event: React.KeyboardEvent) => {
16-
if (event.key === "Escape" || event.key === "Enter" || event.key === " ") {
17-
context.setOpen(false);
18-
}
19-
};
20-
21-
const isClickInteraction = context.interactionType === "click";
15+
const popoverClassName = cn("z-40", props?.className);
2216

2317
return (
2418
<FloatingPortal>
2519
<FloatingFocusManager context={floatingContext} initialFocus={initialFocusElement || 0}>
2620
{context.isMounted ? (
27-
<>
28-
{isClickInteraction ? (
29-
<div
30-
aria-hidden="true"
31-
className="fixed inset-0 z-30 bg-black/10"
32-
onClick={() => context.setOpen(false)}
33-
onKeyDown={handleOverlayKeyDown}
34-
role="button"
35-
tabIndex={0}
36-
/>
37-
) : null}
38-
<div
39-
ref={ref}
40-
style={{ ...style, ...context.floatingStyles, ...context.styles }}
41-
{...context.getFloatingProps(props)}
42-
className={`${props?.className} z-40`}
43-
>
44-
{props.children}
45-
</div>
46-
</>
21+
<div
22+
ref={ref}
23+
style={{ ...style, ...context.floatingStyles, ...context.styles }}
24+
{...context.getFloatingProps(props)}
25+
className={popoverClassName}
26+
>
27+
{props.children}
28+
</div>
4729
) : (
4830
<div />
4931
)}

0 commit comments

Comments
 (0)