Skip to content

Commit 83ac597

Browse files
committed
Merge branch 'main' into brett-60111/add-pagination-for-organizations-members-table
2 parents 4ed07c4 + 30179ae commit 83ac597

File tree

24 files changed

+968
-4
lines changed

24 files changed

+968
-4
lines changed

docs/user-guides/desktop/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ You can install Coder Desktop on macOS or Windows.
3434

3535
1. Continue to the [configuration section](#configure).
3636

37+
> Do not install more than one copy of Coder Desktop.
38+
>
39+
> To avoid system VPN configuration conflicts, only one copy of `Coder Desktop.app` should exist on your Mac, and it must remain in `/Applications`.
40+
3741
### Windows
3842

3943
1. Download the latest `CoderDesktop` installer executable (`.exe`) from the [coder-desktop-windows release page](https://github.com/coder/coder-desktop-windows/releases).

examples/templates/docker/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ module "jetbrains_gateway" {
139139
source = "registry.coder.com/modules/jetbrains-gateway/coder"
140140

141141
# JetBrains IDEs to make available for the user to select
142-
jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
142+
jetbrains_ides = ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"]
143143
default = "IU"
144144

145145
# Default folder to open when starting a JetBrains IDE

site/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@radix-ui/react-dropdown-menu": "2.1.4",
5757
"@radix-ui/react-label": "2.1.0",
5858
"@radix-ui/react-popover": "1.1.5",
59+
"@radix-ui/react-scroll-area": "1.2.3",
5960
"@radix-ui/react-select": "2.1.4",
6061
"@radix-ui/react-slider": "1.2.2",
6162
"@radix-ui/react-slot": "1.1.1",

site/pnpm-lock.yaml

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/components/Button/Button.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const buttonVariants = cva(
3131
lg: "min-w-20 h-10 px-3 py-2 [&_svg]:size-icon-lg",
3232
sm: "min-w-20 h-8 px-2 py-1.5 text-xs [&_svg]:size-icon-sm",
3333
icon: "size-8 px-1.5 [&_svg]:size-icon-sm",
34+
"icon-lg": "size-10 px-2 [&_svg]:size-icon-lg",
3435
},
3536
},
3637
defaultVariants: {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copied from shadc/ui on 03/05/2025
3+
* @see {@link https://ui.shadcn.com/docs/components/scroll-area}
4+
*/
5+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
6+
import * as React from "react";
7+
import { cn } from "utils/cn";
8+
9+
export const ScrollArea = React.forwardRef<
10+
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
11+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
12+
>(({ className, children, ...props }, ref) => (
13+
<ScrollAreaPrimitive.Root
14+
ref={ref}
15+
className={cn("relative overflow-hidden", className)}
16+
{...props}
17+
>
18+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
19+
{children}
20+
</ScrollAreaPrimitive.Viewport>
21+
<ScrollBar />
22+
<ScrollAreaPrimitive.Corner />
23+
</ScrollAreaPrimitive.Root>
24+
));
25+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
26+
27+
export const ScrollBar = React.forwardRef<
28+
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
29+
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
30+
>(({ className, orientation = "vertical", ...props }, ref) => (
31+
<ScrollAreaPrimitive.ScrollAreaScrollbar
32+
ref={ref}
33+
orientation={orientation}
34+
className={cn(
35+
"border-0 border-solid border-border flex touch-none select-none transition-colors",
36+
orientation === "vertical" &&
37+
"h-full w-2.5 border-l border-l-transparent p-[1px]",
38+
orientation === "horizontal" &&
39+
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
40+
className,
41+
)}
42+
{...props}
43+
>
44+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
45+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
46+
));

site/src/components/SearchField/SearchField.stories.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ type Story = StoryObj<typeof SearchField>;
2020

2121
export const Empty: Story = {};
2222

23+
export const Focused: Story = {
24+
args: {
25+
autoFocus: true,
26+
},
27+
};
28+
2329
export const DefaultValue: Story = {
2430
args: {
2531
value: "owner:me",

site/src/components/SearchField/SearchField.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,28 @@ import InputAdornment from "@mui/material/InputAdornment";
66
import TextField, { type TextFieldProps } from "@mui/material/TextField";
77
import Tooltip from "@mui/material/Tooltip";
88
import visuallyHidden from "@mui/utils/visuallyHidden";
9-
import type { FC } from "react";
9+
import { type FC, useEffect, useRef } from "react";
1010

1111
export type SearchFieldProps = Omit<TextFieldProps, "onChange"> & {
1212
onChange: (query: string) => void;
13+
autoFocus?: boolean;
1314
};
1415

1516
export const SearchField: FC<SearchFieldProps> = ({
1617
value = "",
1718
onChange,
19+
autoFocus = false,
1820
InputProps,
1921
...textFieldProps
2022
}) => {
2123
const theme = useTheme();
24+
const inputRef = useRef<HTMLInputElement>(null);
25+
26+
if (autoFocus) {
27+
useEffect(() => {
28+
inputRef.current?.focus();
29+
});
30+
}
2231
return (
2332
<TextField
2433
// Specifying `minWidth` so that the text box can't shrink so much
@@ -27,6 +36,7 @@ export const SearchField: FC<SearchFieldProps> = ({
2736
size="small"
2837
value={value}
2938
onChange={(e) => onChange(e.target.value)}
39+
inputRef={inputRef}
3040
InputProps={{
3141
startAdornment: (
3242
<InputAdornment position="start">
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { InboxButton } from "./InboxButton";
3+
4+
const meta: Meta<typeof InboxButton> = {
5+
title: "modules/notifications/NotificationsInbox/InboxButton",
6+
component: InboxButton,
7+
};
8+
9+
export default meta;
10+
type Story = StoryObj<typeof InboxButton>;
11+
12+
export const AllRead: Story = {};
13+
14+
export const Unread: Story = {
15+
args: {
16+
unreadCount: 3,
17+
},
18+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Button, type ButtonProps } from "components/Button/Button";
2+
import { BellIcon } from "lucide-react";
3+
import { type FC, forwardRef } from "react";
4+
import { UnreadBadge } from "./UnreadBadge";
5+
6+
type InboxButtonProps = {
7+
unreadCount: number;
8+
} & ButtonProps;
9+
10+
export const InboxButton = forwardRef<HTMLButtonElement, InboxButtonProps>(
11+
({ unreadCount, ...props }, ref) => {
12+
return (
13+
<Button
14+
size="icon-lg"
15+
variant="outline"
16+
className="relative"
17+
ref={ref}
18+
{...props}
19+
>
20+
<BellIcon />
21+
{unreadCount > 0 && (
22+
<UnreadBadge
23+
count={unreadCount}
24+
className="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2"
25+
/>
26+
)}
27+
</Button>
28+
);
29+
},
30+
);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { expect, fn, userEvent, within } from "@storybook/test";
3+
import { MockNotification } from "testHelpers/entities";
4+
import { InboxItem } from "./InboxItem";
5+
6+
const meta: Meta<typeof InboxItem> = {
7+
title: "modules/notifications/NotificationsInbox/InboxItem",
8+
component: InboxItem,
9+
render: (args) => {
10+
return (
11+
<div className="max-w-[460px] border-solid border-border rounded">
12+
<InboxItem {...args} />
13+
</div>
14+
);
15+
},
16+
};
17+
18+
export default meta;
19+
type Story = StoryObj<typeof InboxItem>;
20+
21+
export const Read: Story = {
22+
args: {
23+
notification: {
24+
...MockNotification,
25+
read_status: "read",
26+
},
27+
},
28+
};
29+
30+
export const Unread: Story = {
31+
args: {
32+
notification: {
33+
...MockNotification,
34+
read_status: "unread",
35+
},
36+
},
37+
};
38+
39+
export const UnreadFocus: Story = {
40+
args: {
41+
notification: {
42+
...MockNotification,
43+
read_status: "unread",
44+
},
45+
},
46+
play: async ({ canvasElement }) => {
47+
const canvas = within(canvasElement);
48+
const notification = canvas.getByRole("menuitem");
49+
await userEvent.click(notification);
50+
},
51+
};
52+
53+
export const OnMarkNotificationAsRead: Story = {
54+
args: {
55+
notification: {
56+
...MockNotification,
57+
read_status: "unread",
58+
},
59+
onMarkNotificationAsRead: fn(),
60+
},
61+
play: async ({ canvasElement, args }) => {
62+
const canvas = within(canvasElement);
63+
const notification = canvas.getByRole("menuitem");
64+
await userEvent.click(notification);
65+
const markButton = canvas.getByRole("button", { name: /mark as read/i });
66+
await userEvent.click(markButton);
67+
await expect(args.onMarkNotificationAsRead).toHaveBeenCalledTimes(1);
68+
await expect(args.onMarkNotificationAsRead).toHaveBeenCalledWith(
69+
args.notification.id,
70+
);
71+
},
72+
parameters: {
73+
chromatic: {
74+
disableSnapshot: true,
75+
},
76+
},
77+
};

0 commit comments

Comments
 (0)