Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
BrunoQuaresma wants to merge27 commits intomainfrombq/improve-template-filter
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
27 commits
Select commitHold shift + click to select a range
d7a4be0
Add base structure for the new filter component
BrunoQuaresmaMay 15, 2024
f6df4fa
Add error state
BrunoQuaresmaMay 15, 2024
d41d307
Simplify component
BrunoQuaresmaMay 15, 2024
47fd44b
Make it accessible
BrunoQuaresmaMay 15, 2024
d28e9b4
Add simple dropdown button component
BrunoQuaresmaMay 15, 2024
16bbf5a
Refactor search and add preset filter menu
BrunoQuaresmaMay 15, 2024
5d8829e
Add status menu
BrunoQuaresmaMay 15, 2024
c89cf8c
Add status menu to workspaces search
BrunoQuaresmaMay 15, 2024
0c501e5
WIP: Create base UserMenu
BrunoQuaresmaMay 17, 2024
c38cd7b
Finish user menu
BrunoQuaresmaMay 24, 2024
38b10a5
Add search
BrunoQuaresmaMay 24, 2024
3797ae9
Refactor search field to be more extensible
BrunoQuaresmaMay 24, 2024
b552828
Extract reusable menu elements
BrunoQuaresmaMay 24, 2024
498f6cd
Simplify usage
BrunoQuaresmaMay 24, 2024
de83eda
Improve MenuSearch component
BrunoQuaresmaMay 24, 2024
c2aa478
Keep search fixed on top
BrunoQuaresmaMay 24, 2024
30a8373
Add more tests
BrunoQuaresmaMay 24, 2024
542c377
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresmaMay 28, 2024
01a6616
Handle filtering
BrunoQuaresmaMay 28, 2024
e97ce00
Add focus on arrow down
BrunoQuaresmaMay 28, 2024
7638595
Add template menu
BrunoQuaresmaMay 28, 2024
76224c3
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresmaMay 29, 2024
6d1ce99
Use users paginated request
BrunoQuaresmaMay 29, 2024
d3c9ef5
Remove forgotten debuggers
BrunoQuaresmaMay 29, 2024
3250b48
Merge branch 'main' of https://github.com/coder/coder into bq/improve…
BrunoQuaresmaJun 7, 2024
d1b71b1
Remove unecessary prop
BrunoQuaresmaJun 7, 2024
a804a40
Apply michaels review
BrunoQuaresmaJun 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
1 change: 1 addition & 0 deletions.vscode/settings.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -155,6 +155,7 @@
"typesafe",
"unconvert",
"Untar",
"upsert",
"Userspace",
"VMID",
"walkthrough",
Expand Down
9 changes: 4 additions & 5 deletionssite/src/api/queries/templates.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,11 +30,10 @@ export const templateByName = (
};
};

const getTemplatesQueryKey = (organizationId: string, deprecated?: boolean) => [
organizationId,
"templates",
deprecated,
];
export const getTemplatesQueryKey = (
organizationId: string,
deprecated?: boolean,
) => [organizationId, "templates", deprecated];

export const templates = (organizationId: string, deprecated?: boolean) => {
return {
Expand Down
2 changes: 1 addition & 1 deletionsite/src/api/queries/users.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -124,7 +124,7 @@ export const authMethods = () => {
};
};

const meKey = ["me"];
exportconst meKey = ["me"];

export const me = (metadata: MetadataState<User>) => {
return cachedQuery({
Expand Down
9 changes: 9 additions & 0 deletionssite/src/components/Menu/MenuButton.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
import Button, { type ButtonProps } from "@mui/material/Button";
import { forwardRef } from "react";
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";

export const MenuButton = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
return <Button endIcon={<DropdownArrow />} ref={ref} {...props} />;
},
);
17 changes: 17 additions & 0 deletionssite/src/components/Menu/MenuCheck.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +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 (
<MenuIcon>
<CheckOutlined
role="presentation"
css={{
visibility: isVisible ? "visible" : "hidden",
marginLeft: "auto",
}}
/>
</MenuIcon>
);
};
22 changes: 22 additions & 0 deletionssite/src/components/Menu/MenuIcon.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff 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,
}),
});
};
19 changes: 19 additions & 0 deletionssite/src/components/Menu/MenuNoResults.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
import { useTheme } from "@emotion/react";
import type { FC } from "react";

export const MenuNoResults: FC = () => {
const theme = useTheme();

return (
<div
css={{
padding: "8px 12px",
color: theme.palette.text.secondary,
fontSize: 14,
textAlign: "center",
}}
>
No results found
</div>
);
};
43 changes: 43 additions & 0 deletionssite/src/components/Menu/MenuSearch.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
import { css } from "@emotion/css";
import { useTheme } from "@emotion/react";
import type { FC } from "react";
import {
SearchField,
type SearchFieldProps,
} from "components/Search/SearchField";

export const MenuSearch: FC<SearchFieldProps> = (props) => {
const theme = useTheme();

return (
<SearchField
{...props}
onKeyDown={(e) => {
if (e.key === "ArrowDown") {
e.preventDefault();

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

if (firstMenuItem) {
firstMenuItem.focus();
}
}
}}
className={css({
position: "sticky",
top: 0,
backgroundColor: theme.palette.background.paper,
zIndex: 1,
"& fieldset": {
border: 0,
borderRadius: 0,
borderBottomStyle: "solid",
borderBottomWidth: `1px !important`,
borderColor: `${theme.palette.divider} !important`,
},
})}
/>
);
};
15 changes: 14 additions & 1 deletionsite/src/components/Popover/Popover.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -81,6 +81,19 @@ export const usePopover = () => {
return context;
};

export const withPopover =
<TProps extends Record<string, unknown>>(
Component: FC<TProps>,
popoverProps?: Omit<PopoverProps, "children">,
): FC<TProps> =>
(props: TProps) => {
return (
<Popover {...popoverProps}>
<Component {...props} />
</Popover>
);
};

export const PopoverTrigger = (
props: HTMLAttributes<HTMLElement> & { children: TriggerElement },
) => {
Expand DownExpand Up@@ -152,9 +165,9 @@ export const PopoverContent: FC<PopoverContentProps> = ({
marginTop: hoverMode ? undefined : 8,
pointerEvents: hoverMode ? "none" : undefined,
"& .MuiPaper-root": {
minWidth: 320,
fontSize: 14,
pointerEvents: hoverMode ? "auto" : undefined,
borderRadius: 6,
},
}}
{...horizontalProps(horizontal)}
Expand Down
78 changes: 78 additions & 0 deletionssite/src/components/Search/SearchField.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, within, expect } from "@storybook/test";
import { useState } from "react";
import { SearchField } from "./SearchField";

const searchLabel = "Search for something";

const meta: Meta<typeof SearchField> = {
title: "components/SearchField",
component: SearchField,
args: {
id: "search",
label: searchLabel,
},
render: function SearchFieldWithState(args) {
const [value, setValue] = useState<string>(args.value);
return <SearchField {...args} value={value} onChange={setValue} />;
},
};

export default meta;
type Story = StoryObj<typeof SearchField>;

export const Empty: Story = {
args: {
value: "",
},
};

export const DefaultValue: Story = {
args: {
value: "owner:CoderUser",
},
};

export const Error: Story = {
args: {
value: "number_of_users:7",
error: `"number_of_users" is not a valid query param`,
},
};

export const Focused: Story = {
args: {
value: "",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const input = canvas.getByLabelText(searchLabel);
await userEvent.click(input);
await expect(input).toHaveFocus();
},
};

export const Typing: Story = {
args: {
value: "",
},
play: async ({ canvasElement }) => {
const text = "owner:SomeSearchString";
const canvas = within(canvasElement);
const input = canvas.getByLabelText(searchLabel);
await userEvent.type(input, text);
await expect(input).toHaveValue(text);
},
};

export const ClearInput: Story = {
args: {
value: "owner:CoderUser",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const input = canvas.getByLabelText(searchLabel);
await userEvent.click(canvas.getByRole("button", { name: "Clear filter" }));
await expect(input).toHaveValue("");
},
};
83 changes: 83 additions & 0 deletionssite/src/components/Search/SearchField.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
import { useTheme } from "@emotion/react";
import CloseOutlined from "@mui/icons-material/CloseOutlined";
import SearchOutlined from "@mui/icons-material/SearchOutlined";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment";
import TextField, { type TextFieldProps } from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import { visuallyHidden } from "@mui/utils";
import type { FC } from "react";

export type SearchFieldProps = Omit<
TextFieldProps,
"onChange" | "id" | "label" | "value" | "error"
> & {
id: string;
label: string;
value: string;
autoFocus?: boolean;
error?: string;
onChange: (value: string) => void;
};

export const SearchField: FC<SearchFieldProps> = (props) => {
const theme = useTheme();
const { value, label, id, error, onChange, autoFocus, ...textFieldProps } =
props;
const isEmpty = value.length === 0;

return (
<>
<label htmlFor={id} css={{ ...visuallyHidden }}>
{label}
</label>
<TextField
{...textFieldProps}
error={Boolean(error)}
helperText={error}
type="text"
InputProps={{
...textFieldProps.InputProps,
autoFocus,
id,
size: "small",
startAdornment: (
<InputAdornment position="start">
<SearchOutlined
role="presentation"
css={{
fontSize: 14,
color: theme.palette.text.secondary,
}}
/>
</InputAdornment>
),
endAdornment: !isEmpty && (
<Tooltip title="Clear filter">
<IconButton
size="small"
onClick={() => {
onChange("");
}}
>
<CloseOutlined
css={{
fontSize: 14,
color: theme.palette.text.secondary,
}}
/>
<span css={{ ...visuallyHidden }}>Clear filter</span>
</IconButton>
</Tooltip>
),
}}
value={value}
onChange={(e) => {
onChange(e.target.value);
}}
placeholder={textFieldProps.placeholder ?? "Search..."}
fullWidth
/>
</>
);
};
17 changes: 17 additions & 0 deletionssite/src/components/TemplateAvatar/TemplateAvatar.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
import type { FC } from "react";
import type { Template } from "api/typesGenerated";
import { Avatar, type AvatarProps } from "components/Avatar/Avatar";

type TemplateAvatarProps = {
template: Template;
size: AvatarProps["size"];
};

export const TemplateAvatar: FC<TemplateAvatarProps> = ({ template, size }) => {
const hasIcon = template.icon && template.icon !== "";

if (hasIcon) {
return <Avatar size={size} src={template.icon} variant="square" fitImage />;
}
return <Avatar size={size}>{template.name}</Avatar>;
};
10 changes: 2 additions & 8 deletionssite/src/pages/TemplatePage/TemplatePageHeader.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,6 @@ import type {
Template,
TemplateVersion,
} from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { DeleteDialog } from "components/Dialogs/DeleteDialog/DeleteDialog";
import { Margins } from "components/Margins/Margins";
Expand All@@ -33,6 +32,7 @@ import {
} from "components/PageHeader/PageHeader";
import { Pill } from "components/Pill/Pill";
import { Stack } from "components/Stack/Stack";
import { TemplateAvatar } from "components/TemplateAvatar/TemplateAvatar";
import { useDeletionDialogState } from "./useDeletionDialogState";

type TemplateMenuProps = {
Expand DownExpand Up@@ -165,8 +165,6 @@ export const TemplatePageHeader: FC<TemplatePageHeaderProps> = ({
permissions,
onDeleteTemplate,
}) => {
const hasIcon = template.icon && template.icon !== "";

return (
<Margins>
<PageHeader
Expand DownExpand Up@@ -195,11 +193,7 @@ export const TemplatePageHeader: FC<TemplatePageHeaderProps> = ({
}
>
<Stack direction="row" spacing={3} alignItems="center">
{hasIcon ? (
<Avatar size="xl" src={template.icon} variant="square" fitImage />
) : (
<Avatar size="xl">{template.name}</Avatar>
)}
<TemplateAvatar size="xl" template={template} />

<div>
<Stack direction="row" alignItems="center" spacing={1}>
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp