Skip to content

chore(site): refactor filters #13394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d7a4be0
Add base structure for the new filter component
BrunoQuaresma May 15, 2024
f6df4fa
Add error state
BrunoQuaresma May 15, 2024
d41d307
Simplify component
BrunoQuaresma May 15, 2024
47fd44b
Make it accessible
BrunoQuaresma May 15, 2024
d28e9b4
Add simple dropdown button component
BrunoQuaresma May 15, 2024
16bbf5a
Refactor search and add preset filter menu
BrunoQuaresma May 15, 2024
5d8829e
Add status menu
BrunoQuaresma May 15, 2024
c89cf8c
Add status menu to workspaces search
BrunoQuaresma May 15, 2024
0c501e5
WIP: Create base UserMenu
BrunoQuaresma May 17, 2024
c38cd7b
Finish user menu
BrunoQuaresma May 24, 2024
38b10a5
Add search
BrunoQuaresma May 24, 2024
3797ae9
Refactor search field to be more extensible
BrunoQuaresma May 24, 2024
b552828
Extract reusable menu elements
BrunoQuaresma May 24, 2024
498f6cd
Simplify usage
BrunoQuaresma May 24, 2024
de83eda
Improve MenuSearch component
BrunoQuaresma May 24, 2024
c2aa478
Keep search fixed on top
BrunoQuaresma May 24, 2024
30a8373
Add more tests
BrunoQuaresma May 24, 2024
542c377
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresma May 28, 2024
01a6616
Handle filtering
BrunoQuaresma May 28, 2024
e97ce00
Add focus on arrow down
BrunoQuaresma May 28, 2024
7638595
Add template menu
BrunoQuaresma May 28, 2024
76224c3
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresma May 29, 2024
6d1ce99
Use users paginated request
BrunoQuaresma May 29, 2024
d3c9ef5
Remove forgotten debuggers
BrunoQuaresma May 29, 2024
3250b48
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresma Jun 7, 2024
d1b71b1
Remove unecessary prop
BrunoQuaresma Jun 7, 2024
a804a40
Apply michaels review
BrunoQuaresma Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Apply michaels review
  • Loading branch information
BrunoQuaresma committed Jun 7, 2024
commit a804a403e53675926d25dedabc210de6c7850bbf
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
"typesafe",
"unconvert",
"Untar",
"upsert",
"Userspace",
"VMID",
"walkthrough",
Expand Down
19 changes: 10 additions & 9 deletions site/src/components/Menu/MenuCheck.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import CheckOutlined from "@mui/icons-material/CheckOutlined";
import type { FC } from "react";
import { MenuIcon } from "./MenuIcon";

export const MenuCheck: FC<{ isVisible: boolean }> = ({ isVisible }) => {
return (
<CheckOutlined
role="presentation"
css={{
width: 14,
height: 14,
visibility: isVisible ? "visible" : "hidden",
marginLeft: "auto",
}}
/>
<MenuIcon>
<CheckOutlined
role="presentation"
css={{
visibility: isVisible ? "visible" : "hidden",
marginLeft: "auto",
}}
/>
</MenuIcon>
);
};
22 changes: 22 additions & 0 deletions site/src/components/Menu/MenuIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { css } from "@emotion/css";
import {
type FC,
type PropsWithChildren,
type ReactElement,
cloneElement,
} from "react";

type MenuIconProps = {
size?: number;
};

export const MenuIcon: FC<PropsWithChildren<MenuIconProps>> = ({
children,
size = 14,
}) => {
return cloneElement(children as ReactElement, {
className: css({
fontSize: size,
}),
});
};
12 changes: 3 additions & 9 deletions site/src/components/Menu/MenuSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@ export const MenuSearch: FC<SearchFieldProps> = (props) => {
if (e.key === "ArrowDown") {
e.preventDefault();

const popoverContent =
e.currentTarget.closest<HTMLDivElement>(".MuiPaper-root");

if (!popoverContent) {
return;
}

const firstMenuItem =
popoverContent.querySelector<HTMLElement>("[role=menuitem]");
const firstMenuItem = e.currentTarget
.closest<HTMLDivElement>(".MuiPaper-root")
?.querySelector<HTMLElement>("[role=menuitem]");

if (firstMenuItem) {
firstMenuItem.focus();
Expand Down
2 changes: 1 addition & 1 deletion site/src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const usePopover = () => {
};

export const withPopover =
<TProps extends object>(
<TProps extends Record<string, unknown>>(
Component: FC<TProps>,
popoverProps?: Omit<PopoverProps, "children">,
): FC<TProps> =>
Expand Down
2 changes: 1 addition & 1 deletion site/src/components/Search/SearchField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const SearchField: FC<SearchFieldProps> = (props) => {
}}
value={value}
onChange={(e) => {
onChange(e.currentTarget.value);
onChange(e.target.value);
}}
placeholder={textFieldProps.placeholder ?? "Search..."}
fullWidth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const meta: Meta<typeof PresetFiltersMenu> = {
export default meta;
type Story = StoryObj<typeof PresetFiltersMenu>;

export const Close: Story = {};
export const Closed: Story = {};

export const Open: Story = {
play: async ({ canvasElement }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import OpenInNew from "@mui/icons-material/OpenInNew";
import Button from "@mui/material/Button";
import Divider from "@mui/material/Divider";
import MenuItem from "@mui/material/MenuItem";
import MenuList from "@mui/material/MenuList";
import type { FC } from "react";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
import { MenuIcon } from "components/Menu/MenuIcon";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import { docs } from "utils/docs";

const options = [
type PresetOption = Readonly<{
label: string;
value: string;
}>;

const options: PresetOption[] = [
{ label: "My workspaces", value: "owner:me" },
{ label: "All workspaces", value: "" },
{ label: "Running workspaces", value: "status:running" },
Expand Down Expand Up @@ -56,6 +63,9 @@ export const PresetFiltersMenu: FC<PresetFilterMenuProps> = ({ onSelect }) => {
}}
>
Learn advanced filtering
<MenuIcon>
<OpenInNew aria-label="Open in new tab" />
</MenuIcon>
</MenuItem>
</MenuList>
</PopoverContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { StatusMenu } from "./StatusMenu";
const meta: Meta<typeof StatusMenu> = {
title: "pages/WorkspacesPage/StatusMenu",
component: StatusMenu,
args: {
placeholder: "All statuses",
},
};

export default meta;
Expand Down
9 changes: 8 additions & 1 deletion site/src/pages/WorkspacesPage/WorkspaceSearch/StatusMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const StatusIndicator: FC<StatusIndicatorProps> = ({ color }) => {

return (
<div
role="presentation"
css={{
width: 8,
height: 8,
Expand All @@ -29,7 +30,13 @@ const StatusIndicator: FC<StatusIndicatorProps> = ({ color }) => {
);
};

const options = [
type StatusOption = Readonly<{
label: string;
value: string;
indicator: JSX.Element;
}>;

const options: StatusOption[] = [
{
label: "Running",
value: "running",
Expand Down
33 changes: 18 additions & 15 deletions site/src/pages/WorkspacesPage/WorkspaceSearch/TemplateMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ export const TemplateMenu = withPopover<TemplateMenuProps>((props) => {
const popover = usePopover();
const { organizationId, selected, onSelect } = props;
const [filter, setFilter] = useState("");
const templateOptionsQuery = useQuery({
const [isHovered, setIsHovered] = useState(false);
const { data: options } = useQuery({
...templates(organizationId),
enabled: selected !== undefined || popover.isOpen,
enabled: selected !== undefined || popover.isOpen || isHovered,
select: (data) =>
data
.filter((t) => {
const f = filter.toLowerCase();
return (
t.name?.toLowerCase().includes(f) ||
t.display_name.toLowerCase().includes(f)
);
})
.map((t) => ({
label: t.display_name ?? t.name,
value: t.id,
avatar: <TemplateAvatar size="xs" template={t} />,
})),
});
const options = templateOptionsQuery.data
?.filter((t) => {
const f = filter.toLowerCase();
return (
t.name?.toLowerCase().includes(f) ||
t.display_name.toLowerCase().includes(f)
);
})
.map((t) => ({
label: t.display_name ?? t.name,
value: t.id,
avatar: <TemplateAvatar size="xs" template={t} />,
}));
const selectedOption = options?.find((option) => option.value === selected);

return (
Expand All @@ -51,6 +53,7 @@ export const TemplateMenu = withPopover<TemplateMenuProps>((props) => {
<MenuButton
aria-label="Select template"
startIcon={<span>{selectedOption?.avatar}</span>}
onMouseEnter={() => setIsHovered(true)}
>
{selectedOption ? selectedOption.label : "All templates"}
</MenuButton>
Expand Down
17 changes: 9 additions & 8 deletions site/src/pages/WorkspacesPage/WorkspaceSearch/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ export const UserMenu = withPopover<UserMenuProps>((props) => {
const popover = usePopover();
const { selected, onSelect } = props;
const [filter, setFilter] = useState("");
const [isHovered, setIsHovered] = useState(false);
const debouncedFilter = useDebouncedValue(filter, 300);
const usersQueryResult = useQuery({
...usersQuery({ limit: 100, q: debouncedFilter }),
enabled: popover.isOpen,
enabled: popover.isOpen || isHovered,
});
const { data: selectedUser } = useQuery({
queryKey: selectedUserKey(selected ?? ""),
Expand All @@ -57,6 +58,7 @@ export const UserMenu = withPopover<UserMenuProps>((props) => {
<MenuButton
aria-label="Select user"
startIcon={<span>{selectedOption?.avatar}</span>}
onMouseEnter={() => setIsHovered(true)}
>
{selectedOption ? selectedOption.label : "All users"}
</MenuButton>
Expand Down Expand Up @@ -92,7 +94,10 @@ export const UserMenu = withPopover<UserMenuProps>((props) => {

// This avoid the need to refetch the selected user query
// when the user is selected
setSelectedUserQueryData(user, queryClient);
queryClient.setQueryData(
selectedUserKey(user.email),
user,
);
popover.setIsOpen(false);
onSelect(option.value);
}}
Expand Down Expand Up @@ -133,10 +138,6 @@ async function getSelectedUser(
return usersRes.users.at(0);
}

function setSelectedUserQueryData(user: User, queryClient: QueryClient) {
queryClient.setQueryData(selectedUserKey(user.email), user);
}

function optionFromUser(user: User): UserOption {
return {
label: user.name ?? user.username,
Expand All @@ -150,12 +151,12 @@ function optionFromUser(user: User): UserOption {
function mountOptions(
users: readonly User[] | undefined,
selectedUser: User | undefined,
): UserOption[] | undefined {
): Readonly<UserOption>[] | undefined {
if (!users) {
return undefined;
}

let usersToDisplay = [...users];
let usersToDisplay = users;

if (selectedUser) {
const usersIncludeSelectedUser = users.some(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const WorkspaceSearch: FC<WorkspaceSearchProps> = ({
<StatusMenu
selected={status}
onSelect={(status) => {
setQuery(replaceOrAddTagValue(query, "status", status));
setQuery(upsertTagValue(query, "status", status));
}}
/>
</Stack>
Expand All @@ -56,11 +56,7 @@ function findTagValue(query: string, tag: string): string | undefined {
return block.split(":")[1];
}

function replaceOrAddTagValue(
query: string,
tag: string,
value: string,
): string {
function upsertTagValue(query: string, tag: string, value: string): string {
const blocks = query.split(" ");
const block = blocks.find((block) => block.startsWith(`${tag}:`));

Expand Down
4 changes: 0 additions & 4 deletions site/src/theme/mui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,6 @@ export const components = {
styleOverrides: {
root: {
gap: 12,

"& .MuiSvgIcon-root": {
fontSize: 20,
},
},
},
},
Expand Down
Loading