Skip to content

Commit 1be119b

Browse files
fix(site): fix search menu for creating workspace and templates filter (#11674)
1 parent b246f08 commit 1be119b

File tree

4 files changed

+131
-103
lines changed

4 files changed

+131
-103
lines changed

site/src/components/Filter/filter.tsx

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ import { Loader } from "components/Loader/Loader";
3232
import { useDebouncedFunction } from "hooks/debounce";
3333
import { useFilterMenu } from "./menu";
3434
import type { BaseOption } from "./options";
35+
import {
36+
Search,
37+
SearchEmpty,
38+
SearchInput,
39+
searchStyles,
40+
} from "components/Menu/Search";
3541

3642
export type PresetFilter = {
3743
name: string;
@@ -489,7 +495,7 @@ export const FilterSearchMenu = <TOption extends BaseOption>({
489495
onQueryChange={menu.setQuery}
490496
renderOption={(option) => (
491497
<MenuItem
492-
key={option.label}
498+
key={option.value}
493499
selected={option.value === menu.selectedOption?.value}
494500
onClick={() => {
495501
menu.selectOption(option);
@@ -576,7 +582,6 @@ function SearchMenu<TOption extends BaseOption>({
576582
}: SearchMenuProps<TOption>) {
577583
const menuListRef = useRef<HTMLUListElement>(null);
578584
const searchInputRef = useRef<HTMLInputElement>(null);
579-
const theme = useTheme();
580585

581586
return (
582587
<Menu
@@ -586,10 +591,7 @@ function SearchMenu<TOption extends BaseOption>({
586591
onQueryChange("");
587592
}}
588593
css={{
589-
"& .MuiPaper-root": {
590-
width: 320,
591-
padding: 0,
592-
},
594+
"& .MuiPaper-root": searchStyles.content,
593595
}}
594596
// Disabled this so when we clear the filter and do some sorting in the
595597
// search items it does not look strange. Github removes exit transitions
@@ -606,44 +608,16 @@ function SearchMenu<TOption extends BaseOption>({
606608
}
607609
}}
608610
>
609-
<li
610-
css={{
611-
display: "flex",
612-
alignItems: "center",
613-
paddingLeft: 16,
614-
height: 40,
615-
borderBottom: `1px solid ${theme.palette.divider}`,
616-
}}
617-
>
618-
<SearchOutlined
619-
css={{
620-
fontSize: 14,
621-
color: theme.palette.text.secondary,
622-
}}
623-
/>
624-
<input
625-
tabIndex={-1}
626-
type="text"
627-
placeholder="Search..."
611+
<Search component="li">
612+
<SearchInput
628613
autoFocus
629614
value={query}
630615
ref={searchInputRef}
631616
onChange={(e) => {
632617
onQueryChange(e.target.value);
633618
}}
634-
css={{
635-
height: "100%",
636-
border: 0,
637-
background: "none",
638-
width: "100%",
639-
marginLeft: 16,
640-
outline: 0,
641-
"&::placeholder": {
642-
color: theme.palette.text.secondary,
643-
},
644-
}}
645619
/>
646-
</li>
620+
</Search>
647621

648622
<li css={{ maxHeight: 480, overflowY: "auto" }}>
649623
<MenuList
@@ -660,17 +634,7 @@ function SearchMenu<TOption extends BaseOption>({
660634
options.length > 0 ? (
661635
options.map(renderOption)
662636
) : (
663-
<div
664-
css={{
665-
fontSize: 13,
666-
color: theme.palette.text.secondary,
667-
textAlign: "center",
668-
paddingTop: 8,
669-
paddingBottom: 8,
670-
}}
671-
>
672-
No results
673-
</div>
637+
<SearchEmpty />
674638
)
675639
) : (
676640
<Loader size={14} />

site/src/components/Menu/Search.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import SearchOutlined from "@mui/icons-material/SearchOutlined";
2+
// eslint-disable-next-line no-restricted-imports -- use it to have the component prop
3+
import Box, { BoxProps } from "@mui/material/Box";
4+
import { Interpolation, Theme, useTheme } from "@mui/material/styles";
5+
import visuallyHidden from "@mui/utils/visuallyHidden";
6+
import { FC, HTMLAttributes, InputHTMLAttributes, forwardRef } from "react";
7+
8+
export const Search = forwardRef<HTMLElement, BoxProps>(
9+
({ children, ...boxProps }, ref) => {
10+
const theme = useTheme();
11+
12+
return (
13+
<Box
14+
ref={ref}
15+
{...boxProps}
16+
css={{
17+
display: "flex",
18+
alignItems: "center",
19+
paddingLeft: 16,
20+
height: 40,
21+
borderBottom: `1px solid ${theme.palette.divider}`,
22+
}}
23+
>
24+
<SearchOutlined
25+
css={{
26+
fontSize: 14,
27+
color: theme.palette.text.secondary,
28+
}}
29+
/>
30+
{children}
31+
</Box>
32+
);
33+
},
34+
);
35+
36+
type SearchInputProps = InputHTMLAttributes<HTMLInputElement> & {
37+
label?: string;
38+
};
39+
40+
export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
41+
({ label, ...inputProps }, ref) => {
42+
const theme = useTheme();
43+
44+
return (
45+
<>
46+
<label css={{ ...visuallyHidden }} htmlFor={inputProps.id}>
47+
{label}
48+
</label>
49+
<input
50+
ref={ref}
51+
tabIndex={-1}
52+
type="text"
53+
placeholder="Search..."
54+
css={{
55+
height: "100%",
56+
border: 0,
57+
background: "none",
58+
flex: 1,
59+
marginLeft: 16,
60+
outline: 0,
61+
"&::placeholder": {
62+
color: theme.palette.text.secondary,
63+
},
64+
}}
65+
{...inputProps}
66+
/>
67+
</>
68+
);
69+
},
70+
);
71+
72+
export const SearchEmpty: FC<HTMLAttributes<HTMLDivElement>> = ({
73+
children = "Not found",
74+
...props
75+
}) => {
76+
const theme = useTheme();
77+
78+
return (
79+
<div
80+
css={{
81+
fontSize: 13,
82+
color: theme.palette.text.secondary,
83+
textAlign: "center",
84+
paddingTop: 8,
85+
paddingBottom: 8,
86+
}}
87+
{...props}
88+
>
89+
{children}
90+
</div>
91+
);
92+
};
93+
94+
export const searchStyles = {
95+
content: {
96+
width: 320,
97+
padding: 0,
98+
borderRadius: 4,
99+
},
100+
} satisfies Record<string, Interpolation<Theme>>;

site/src/pages/WorkspacesPage/WorkspacesButton.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import AddIcon from "@mui/icons-material/AddOutlined";
1111
import OpenIcon from "@mui/icons-material/OpenInNewOutlined";
1212
import { Loader } from "components/Loader/Loader";
1313
import { OverflowY } from "components/OverflowY/OverflowY";
14-
import { EmptyState } from "components/EmptyState/EmptyState";
1514
import { Avatar } from "components/Avatar/Avatar";
1615
import { SearchBox } from "./WorkspacesSearchBox";
1716
import {
1817
Popover,
1918
PopoverContent,
2019
PopoverTrigger,
2120
} from "components/Popover/Popover";
21+
import { SearchEmpty, searchStyles } from "components/Menu/Search";
2222

2323
const ICON_SIZE = 18;
2424

@@ -43,17 +43,15 @@ export const WorkspacesButton: FC<WorkspacesButtonProps> = ({
4343
let emptyState: ReactNode = undefined;
4444
if (templates?.length === 0) {
4545
emptyState = (
46-
<EmptyState
47-
message="No templates yet"
48-
cta={
49-
<Link to="/templates" component={RouterLink}>
50-
Create one now.
51-
</Link>
52-
}
53-
/>
46+
<SearchEmpty>
47+
No templates yet.{" "}
48+
<Link to="/templates" component={RouterLink}>
49+
Create one now.
50+
</Link>
51+
</SearchEmpty>
5452
);
5553
} else if (processed.length === 0) {
56-
emptyState = <EmptyState message="No templates match your text" />;
54+
emptyState = <SearchEmpty>No templates found</SearchEmpty>;
5755
}
5856

5957
return (
@@ -63,7 +61,12 @@ export const WorkspacesButton: FC<WorkspacesButtonProps> = ({
6361
{children}
6462
</Button>
6563
</PopoverTrigger>
66-
<PopoverContent horizontal="right">
64+
<PopoverContent
65+
horizontal="right"
66+
css={{
67+
".MuiPaper-root": searchStyles.content,
68+
}}
69+
>
6770
<SearchBox
6871
value={searchTerm}
6972
onValueChange={(newValue) => setSearchTerm(newValue)}

site/src/pages/WorkspacesPage/WorkspacesSearchBox.tsx

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import {
1111
forwardRef,
1212
useId,
1313
} from "react";
14-
import SearchIcon from "@mui/icons-material/SearchOutlined";
15-
import { visuallyHidden } from "@mui/utils";
16-
import { useTheme } from "@emotion/react";
14+
import { Search, SearchInput } from "components/Menu/Search";
1715

1816
interface SearchBoxProps extends InputHTMLAttributes<HTMLInputElement> {
1917
label?: string;
@@ -35,39 +33,12 @@ export const SearchBox = forwardRef(function SearchBox(
3533
} = props;
3634

3735
const hookId = useId();
38-
const theme = useTheme();
39-
4036
const inputId = `${hookId}-${SearchBox.name}-input`;
4137

4238
return (
43-
<div
44-
css={{
45-
display: "flex",
46-
flexFlow: "row nowrap",
47-
alignItems: "center",
48-
padding: "0 8px",
49-
height: "40px",
50-
borderBottom: `1px solid ${theme.palette.divider}`,
51-
}}
52-
>
53-
<div css={{ width: 18 }}>
54-
<SearchIcon
55-
css={{
56-
display: "block",
57-
fontSize: "14px",
58-
marginLeft: "auto",
59-
marginRight: "auto",
60-
color: theme.palette.text.secondary,
61-
}}
62-
/>
63-
</div>
64-
65-
<label css={{ ...visuallyHidden }} htmlFor={inputId}>
66-
{label}
67-
</label>
68-
69-
<input
70-
type="text"
39+
<Search>
40+
<SearchInput
41+
label={label}
7142
ref={ref}
7243
id={inputId}
7344
autoFocus
@@ -76,17 +47,7 @@ export const SearchBox = forwardRef(function SearchBox(
7647
{...attrs}
7748
onKeyDown={onKeyDown}
7849
onChange={(e) => onValueChange(e.target.value)}
79-
css={{
80-
height: "100%",
81-
border: 0,
82-
background: "none",
83-
width: "100%",
84-
outline: 0,
85-
"&::placeholder": {
86-
color: theme.palette.text.secondary,
87-
},
88-
}}
8950
/>
90-
</div>
51+
</Search>
9152
);
9253
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy