Skip to content

Commit 2fab4ba

Browse files
authored
feat: Dashboards search (#4781)
## Description - [ ] Search the projects - [ ] Use arrow keys directly from the search or by focusing the project - [ ] Hit enter to open - [ ] Empty state when nothing found ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent c0b73ba commit 2fab4ba

File tree

24 files changed

+702
-540
lines changed

24 files changed

+702
-540
lines changed

apps/builder/app/auth/login.tsx

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { SecretLogin } from "./secret-login";
1515
const globalStyles = globalCss({
1616
body: {
1717
margin: 0,
18-
background: theme.colors.brandBackgroundDashboard,
1918
overflow: "hidden",
2019
},
2120
});
@@ -35,7 +34,14 @@ export const Login = ({
3534
}: LoginProps) => {
3635
globalStyles();
3736
return (
38-
<Flex align="center" justify="center" css={{ height: "100vh" }}>
37+
<Flex
38+
align="center"
39+
justify="center"
40+
css={{
41+
height: "100vh",
42+
background: theme.colors.brandBackgroundDashboard,
43+
}}
44+
>
3945
<Flex
4046
direction="column"
4147
align="center"
@@ -56,34 +62,30 @@ export const Login = ({
5662
</Text>
5763

5864
<TooltipProvider>
59-
<Flex
60-
as={Form}
61-
method="post"
62-
direction="column"
63-
gap="3"
64-
css={{ width: "100%" }}
65-
>
66-
<Button
67-
disabled={isGoogleEnabled === false}
68-
prefix={<GoogleIcon size={22} />}
69-
color="primary"
70-
css={{ height: theme.spacing[15] }}
71-
formAction={authPath({ provider: "google" })}
72-
>
73-
Sign in with Google
74-
</Button>
75-
<Button
76-
disabled={isGithubEnabled === false}
77-
prefix={<GithubIcon size={22} fill="currentColor" />}
78-
color="ghost"
79-
css={{
80-
border: `1px solid ${theme.colors.borderDark}`,
81-
height: theme.spacing[15],
82-
}}
83-
formAction={authPath({ provider: "github" })}
84-
>
85-
Sign in with GitHub
86-
</Button>
65+
<Flex direction="column" gap="3" css={{ width: "100%" }}>
66+
<Form method="post" style={{ display: "contents" }}>
67+
<Button
68+
disabled={isGoogleEnabled === false}
69+
prefix={<GoogleIcon size={22} />}
70+
color="primary"
71+
css={{ height: theme.spacing[15] }}
72+
formAction={authPath({ provider: "google" })}
73+
>
74+
Sign in with Google
75+
</Button>
76+
<Button
77+
disabled={isGithubEnabled === false}
78+
prefix={<GithubIcon size={22} fill="currentColor" />}
79+
color="ghost"
80+
css={{
81+
border: `1px solid ${theme.colors.borderDark}`,
82+
height: theme.spacing[15],
83+
}}
84+
formAction={authPath({ provider: "github" })}
85+
>
86+
Sign in with GitHub
87+
</Button>
88+
</Form>
8789
{isSecretLoginEnabled && <SecretLogin />}
8890
</Flex>
8991
</TooltipProvider>

apps/builder/app/auth/secret-login.tsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,25 @@ import { authPath } from "~/shared/router-utils";
55
export const SecretLogin = () => {
66
const [show, setShow] = useState(false);
77
if (show) {
8-
const action = authPath({ provider: "dev" });
98
return (
10-
<Flex gap="2">
11-
<InputField
12-
name="secret"
13-
type="password"
14-
minLength={2}
15-
required
16-
autoFocus
17-
placeholder="Auth secret"
18-
css={{ flexGrow: 1 }}
19-
formAction={authPath({ provider: "dev" })}
20-
onKeyDown={(event) => {
21-
const form = event.currentTarget.form;
22-
if (event.key === "Enter" && form) {
23-
form.action = action;
24-
form.submit();
25-
}
26-
}}
27-
/>
28-
<Button type="submit" formAction={action}>
29-
Login
30-
</Button>
31-
</Flex>
9+
<form
10+
method="post"
11+
action={authPath({ provider: "dev" })}
12+
style={{ display: "contents" }}
13+
>
14+
<Flex gap="2">
15+
<InputField
16+
name="secret"
17+
type="password"
18+
minLength={2}
19+
required
20+
autoFocus
21+
placeholder="Auth secret"
22+
css={{ flexGrow: 1 }}
23+
/>
24+
<Button type="submit">Login</Button>
25+
</Flex>
26+
</form>
3227
);
3328
}
3429

apps/builder/app/builder/features/components/components.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ export const ComponentsPanel = ({
219219

220220
const searchFieldProps = useSearchFieldKeys({
221221
onChange: resetSelectedComponent,
222-
onCancel: resetSelectedComponent,
222+
onAbort: resetSelectedComponent,
223223
onMove({ direction }) {
224224
if (direction === "current") {
225225
const component = getSelectedComponent();
Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type { StoryFn } from "@storybook/react";
22
import type { JSX } from "react";
3-
import { createBrowserRouter, RouterProvider } from "react-router-dom";
4-
import { Dashboard } from "./dashboard";
3+
import { createMemoryRouter, RouterProvider } from "react-router-dom";
4+
import { Dashboard, DashboardSetup } from "./dashboard";
55
import type { UserPlanFeatures } from "~/shared/db/user-plan-features.server";
66
import type { DashboardProject } from "@webstudio-is/dashboard";
77

88
export default {
9-
title: "Dashboard / Projects",
9+
title: "Dashboard",
1010
component: Dashboard,
1111
};
1212

@@ -20,14 +20,10 @@ const user = {
2020
provider: "github",
2121
};
2222

23-
const createRouter = (element: JSX.Element) =>
24-
createBrowserRouter([
25-
{
26-
path: "*",
27-
element,
28-
loader: () => null,
29-
},
30-
]);
23+
const createRouter = (element: JSX.Element, path: string, current?: string) =>
24+
createMemoryRouter([{ path, element }], {
25+
initialEntries: [current ?? path],
26+
});
3127

3228
const userPlanFeatures: UserPlanFeatures = {
3329
hasProPlan: false,
@@ -38,60 +34,85 @@ const userPlanFeatures: UserPlanFeatures = {
3834
maxDomainsAllowedPerUser: 1,
3935
};
4036

41-
export const WithProjects: StoryFn<typeof Dashboard> = () => {
42-
const projects = [
43-
{
44-
id: "0",
45-
createdAt: new Date().toString(),
46-
title: "My Project",
47-
domain: "domain.com",
48-
userId: "",
49-
isDeleted: false,
50-
isPublished: false,
51-
latestBuild: null,
52-
previewImageAsset: null,
53-
previewImageAssetId: "",
54-
latestBuildVirtual: null,
55-
marketplaceApprovalStatus: "UNLISTED" as const,
56-
} as DashboardProject,
57-
];
37+
const projects = [
38+
{
39+
id: "0",
40+
createdAt: new Date().toString(),
41+
title: "My Project",
42+
domain: "domain.com",
43+
userId: "",
44+
isDeleted: false,
45+
isPublished: false,
46+
latestBuild: null,
47+
previewImageAsset: null,
48+
previewImageAssetId: "",
49+
latestBuildVirtual: null,
50+
marketplaceApprovalStatus: "UNLISTED" as const,
51+
} as DashboardProject,
52+
];
53+
54+
const data = {
55+
user,
56+
templates: projects,
57+
userPlanFeatures,
58+
publisherHost: "https://wstd.work",
59+
projects,
60+
};
61+
62+
export const Welcome: StoryFn<typeof Dashboard> = () => {
63+
const router = createRouter(
64+
<>
65+
<DashboardSetup data={{ ...data, projects: [] }} />
66+
<Dashboard />
67+
</>,
68+
"/dashboard/templates"
69+
);
70+
return <RouterProvider router={router} />;
71+
};
72+
73+
export const Projects: StoryFn<typeof Dashboard> = () => {
5874
const router = createRouter(
59-
<Dashboard
60-
user={user}
61-
welcome={false}
62-
projects={projects}
63-
userPlanFeatures={userPlanFeatures}
64-
publisherHost={"https://wstd.work"}
65-
/>
75+
<>
76+
<DashboardSetup data={data} />
77+
<Dashboard />
78+
</>,
79+
"/dashboard"
6680
);
6781
return <RouterProvider router={router} />;
6882
};
6983

70-
export const WithTemplates: StoryFn<typeof Dashboard> = () => {
71-
const templates = [
72-
{
73-
id: "0",
74-
createdAt: new Date().toString(),
75-
title: "My Project",
76-
domain: "domain.com",
77-
userId: "",
78-
isDeleted: false,
79-
isPublished: false,
80-
latestBuild: null,
81-
previewImageAsset: null,
82-
previewImageAssetId: "",
83-
latestBuildVirtual: null,
84-
marketplaceApprovalStatus: "UNLISTED" as const,
85-
} as DashboardProject,
86-
];
84+
export const Templates: StoryFn<typeof Dashboard> = () => {
85+
const router = createRouter(
86+
<>
87+
<DashboardSetup data={data} />
88+
<Dashboard />
89+
</>,
90+
"/dashboard/templates"
91+
);
92+
return <RouterProvider router={router} />;
93+
};
94+
95+
export const Search: StoryFn<typeof Dashboard> = () => {
96+
const router = createRouter(
97+
<>
98+
<DashboardSetup data={data} />
99+
<Dashboard />
100+
</>,
101+
"/dashboard/search",
102+
"/dashboard/search?q=my"
103+
);
104+
105+
return <RouterProvider router={router} />;
106+
};
107+
108+
export const SearchNothingFound: StoryFn<typeof Dashboard> = () => {
87109
const router = createRouter(
88-
<Dashboard
89-
user={user}
90-
templates={templates}
91-
welcome
92-
userPlanFeatures={userPlanFeatures}
93-
publisherHost={"https://wstd.work"}
94-
/>
110+
<>
111+
<DashboardSetup data={data} />
112+
<Dashboard />
113+
</>,
114+
"/dashboard/search",
115+
"/dashboard/search?q=notfound"
95116
);
96117
return <RouterProvider router={router} />;
97118
};

0 commit comments

Comments
 (0)