diff --git a/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.stories.tsx b/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.stories.tsx index de1eb4ad987d6..979ff10051ff4 100644 --- a/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.stories.tsx @@ -58,6 +58,21 @@ export const DownloadingSkill: Story = { args: { downloadingSkillName: "review-sql", }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); + const row = canvas.getByRole("row", { name: /review-sql/ }); + await userEvent.click( + within(row).getByRole("button", { name: "Open menu" }), + ); + const menu = await body.findByRole("menu"); + await expect( + within(menu).getByRole("menuitem", { name: "Download" }), + ).toHaveAttribute("aria-disabled", "true"); + await expect( + within(menu).getByRole("menuitem", { name: "Edit" }), + ).not.toHaveAttribute("aria-disabled"); + }, }; export const ExportingAll: Story = { @@ -66,19 +81,46 @@ export const ExportingAll: Story = { }, }; -export const DownloadsSkill: Story = { +export const RowMenuActions: Story = { play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); + const body = within(canvasElement.ownerDocument.body); const row = canvas.getByRole("row", { name: /review-sql/ }); + const trigger = within(row).getByRole("button", { name: "Open menu" }); + + await userEvent.click(trigger); await userEvent.click( - within(row).getByRole("button", { name: "Download" }), + within(await body.findByRole("menu")).getByRole("menuitem", { + name: "Download", + }), ); - await waitFor(() => { expect(args.onDownload).toHaveBeenCalledWith( expect.objectContaining({ name: "review-sql" }), ); }); + + await userEvent.click(trigger); + await userEvent.click( + within(await body.findByRole("menu")).getByRole("menuitem", { + name: "Edit", + }), + ); + await waitFor(() => { + expect(args.onEdit).toHaveBeenCalledWith("review-sql"); + }); + + await userEvent.click(trigger); + await userEvent.click( + within(await body.findByRole("menu")).getByRole("menuitem", { + name: /Delete/, + }), + ); + await waitFor(() => { + expect(args.onDelete).toHaveBeenCalledWith( + expect.objectContaining({ name: "review-sql" }), + ); + }); }, }; @@ -113,6 +155,19 @@ export const ListError: Story = { }, }; +export const RefetchErrorKeepsRows: Story = { + args: { + error: new Error("Failed to load personal skills."), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await expect(canvas.getByRole("row", { name: /review-sql/ })).toBeVisible(); + await expect( + canvas.getByText("Failed to load personal skills."), + ).toBeVisible(); + }, +}; + export const CreateDialogOpen: Story = { args: { editorState: { diff --git a/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.tsx b/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.tsx index 62ebaf71585cb..33ec027a0ee68 100644 --- a/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.tsx +++ b/site/src/pages/AgentsPage/AgentSettingsPersonalSkillsPageView.tsx @@ -1,3 +1,4 @@ +import { EllipsisVerticalIcon, PlusIcon } from "lucide-react"; import type { FC } from "react"; import type { UserSkillMetadata } from "#/api/typesGenerated"; import { Alert, AlertDescription } from "#/components/Alert/Alert"; @@ -12,7 +13,13 @@ import { DialogHeader, DialogTitle, } from "#/components/Dialog/Dialog"; -import { EmptyState } from "#/components/EmptyState/EmptyState"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "#/components/DropdownMenu/DropdownMenu"; import { Loader } from "#/components/Loader/Loader"; import { Spinner } from "#/components/Spinner/Spinner"; import { @@ -23,6 +30,8 @@ import { TableHeader, TableRow, } from "#/components/Table/Table"; +import { TableEmpty } from "#/components/TableEmpty/TableEmpty"; +import { TableLoader } from "#/components/TableLoader/TableLoader"; import { formatDate } from "#/utils/time"; import type { PersonalSkillErrorDisplay } from "./components/PersonalSkillEditor"; import { PersonalSkillEditor } from "./components/PersonalSkillEditor"; @@ -221,14 +230,18 @@ export const AgentSettingsPersonalSkillsPageView: FC< }) => { const isAtLimit = skills.length >= PERSONAL_SKILLS_MAX_PER_USER; const addSkillAction = ( - ); const headerActions = (
-
- ) : isLoading ? ( - - ) : skills.length === 0 ? ( - - ) : ( - - - - Name - Description - Updated - Actions - - - - {skills.map((skill) => ( + {Boolean(error) && } + +
+ + + Name + Description + Updated + + Actions + + + + + {isLoading ? ( + + ) : skills.length === 0 && error ? ( + + {isRetrying && } + Retry + + } + /> + ) : skills.length === 0 ? ( + + ) : ( + skills.map((skill) => ( - - {skill.name} - + {skill.name} {skill.description || ( - + No description )} {formatUpdatedAt(skill.updated_at)} - -
- - - -
+ + + + + + + onDownload(skill)} + disabled={downloadingSkillName === skill.name} + > + Download + + onEdit(skill.name)}> + Edit + + + onDelete(skill)} + > + Delete… + + +
- ))} -
-
- )} + )) + )} + + {editorState?.mode === "create" && (