From 27692b6332c31d126edd314d2309d476ceb3dc75 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 16 Oct 2026 13:12:39 +0000 Subject: [PATCH 1/3] feat(site/src): edit chat project templates from the project details The project page's Details section gains a Templates row that shows how many templates the project allows and opens a dialog to edit them and their defaults. MultiSelectCombobox now shows a value passed on its first render. --- .../MultiSelectCombobox.tsx | 4 +- .../dialogs/ChatProjectDialog.tsx | 10 +- .../dialogs/ChatProjectTemplatesFields.tsx | 14 +- .../ProjectPage/ProjectDetailsPanel.test.tsx | 108 ++++++++++++++ .../ProjectPage/ProjectDetailsPanel.tsx | 24 ++- .../ProjectDetailsPanelView.stories.tsx | 10 ++ .../ProjectPage/ProjectDetailsPanelView.tsx | 22 +++ .../ProjectTemplatesDialog.stories.tsx | 138 ++++++++++++++++++ .../ProjectPage/ProjectTemplatesDialog.tsx | 121 +++++++++++++++ 9 files changed, 440 insertions(+), 11 deletions(-) create mode 100644 site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.stories.tsx create mode 100644 site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.tsx diff --git a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx index 8e440018691..ce5e78acbc8 100644 --- a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx +++ b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx @@ -206,7 +206,9 @@ export const MultiSelectCombobox: React.FC = ({ const dropdownRef = useRef(null); const listRef = useRef(null); - const [selected, setSelected] = useState(arrayDefaultOptions ?? []); + const [selected, setSelected] = useState( + value ?? arrayDefaultOptions ?? [], + ); const [options, setOptions] = useState( transitionToGroupOption(arrayDefaultOptions, groupBy), ); diff --git a/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectDialog.tsx b/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectDialog.tsx index 8d973b57266..c988b772b10 100644 --- a/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectDialog.tsx +++ b/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectDialog.tsx @@ -33,6 +33,7 @@ import { type ChatProjectTemplatesValues, chatProjectTemplatesRequest, chatProjectTemplatesValues, + templatesValidationDetail, } from "./ChatProjectTemplatesFields"; /** @public */ @@ -249,13 +250,6 @@ const ChatProjectForm: React.FC = ({ const selectedOrganization = organizations.find( (organization) => organization.id === form.values.organizationId, ); - // The form has no field named after these validations, so the templates - // section shows them itself. - const templatesSaveError = isApiValidationError(error) - ? error.response.data.validations?.find((validation) => - validation.field.startsWith("templates."), - )?.detail - : undefined; const templatesOrganizationId = project ? undefined : selectedOrganization?.id; @@ -357,7 +351,7 @@ const ChatProjectForm: React.FC = ({ { void form.setFieldValue("templates", templates); diff --git a/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectTemplatesFields.tsx b/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectTemplatesFields.tsx index d289660a7f2..fb874ebb2d8 100644 --- a/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectTemplatesFields.tsx +++ b/site/src/pages/AgentsPage/components/ChatsSidebar/dialogs/ChatProjectTemplatesFields.tsx @@ -1,6 +1,6 @@ import { useId } from "react"; import { useQuery } from "react-query"; -import { getErrorMessage } from "#/api/errors"; +import { getErrorMessage, isApiValidationError } from "#/api/errors"; import { richParameters, templateVersionPresets, @@ -74,6 +74,18 @@ export const chatProjectTemplatesRequest = ( }; }; +/** + * Returns the server's rejection of the templates in a save error. Forms + * have no field named after these validations, so the templates fields show + * it through `saveError`. + */ +export const templatesValidationDetail = (error: unknown) => + isApiValidationError(error) + ? error.response.data.validations?.find((validation) => + validation.field.startsWith("templates."), + )?.detail + : undefined; + const templateLabel = (template: Template) => template.display_name || template.name; diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx index fb8431878ae..b4071b90252 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx @@ -11,12 +11,14 @@ import { chatProjectInstructionsKey } from "#/api/queries/chatProjects"; import type { ChatProject, ChatProjectInstructions, + Template, } from "#/api/typesGenerated"; import { TooltipProvider } from "#/components/Tooltip/Tooltip"; import { ThemeOverride } from "#/contexts/ThemeProvider"; import { MockChatProject, MockChatProjectInstructions, + MockTemplate, MockUnsetChatProjectInstructions, mockApiError, } from "#/testHelpers/entities"; @@ -471,3 +473,109 @@ describe("ProjectDetailsPanel", () => { await screen.findByRole("button", { name: "Edit instructions" }); }); }); + +const dockerTemplate: Template = { + ...MockTemplate, + id: "template-docker", + name: "docker", + display_name: "Docker", +}; +const kubernetesTemplate: Template = { + ...MockTemplate, + id: "template-kubernetes", + name: "kubernetes", + display_name: "Kubernetes", +}; + +describe("ProjectDetailsPanel templates", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + const setup = () => { + mockInstructions(MockUnsetChatProjectInstructions); + vi.spyOn(API, "getTemplates").mockResolvedValue([ + dockerTemplate, + kubernetesTemplate, + ]); + const updateProject = vi + .spyOn(API.experimental, "updateChatProject") + .mockResolvedValue(MockChatProject); + return { updateProject, ...renderPanel() }; + }; + + it("saves the templates picked in the editor", async () => { + const { user, updateProject } = setup(); + + await user.click( + screen.getByRole("button", { name: "Edit templates: 0 templates" }), + ); + await user.click(await screen.findByRole("button", { name: "Select all" })); + await user.click(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => + expect(updateProject).toHaveBeenCalledWith( + MockChatProject.organization_id, + MockChatProject.id, + { + templates: { + template_ids: [dockerTemplate.id, kubernetesTemplate.id], + }, + }, + ), + ); + }); + + it("starts the editor from the saved templates", async () => { + const { user, updateProject, rerenderWithProject } = setup(); + rerenderWithProject({ + ...MockChatProject, + templates: { + template_ids: [dockerTemplate.id, kubernetesTemplate.id], + }, + }); + + await user.click( + screen.getByRole("button", { name: "Edit templates: 2 templates" }), + ); + await user.click( + await screen.findByRole("combobox", { name: "Templates" }), + ); + await user.keyboard("{Backspace}"); + await user.click(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => + expect(updateProject).toHaveBeenCalledWith( + MockChatProject.organization_id, + MockChatProject.id, + { templates: { template_ids: [dockerTemplate.id] } }, + ), + ); + }); + + it("keeps the draft when the project refetches while editing", async () => { + const { user, updateProject, rerenderWithProject } = setup(); + + await user.click( + screen.getByRole("button", { name: "Edit templates: 0 templates" }), + ); + await user.click(await screen.findByRole("button", { name: "Select all" })); + rerenderWithProject({ + ...MockChatProject, + templates: { template_ids: [kubernetesTemplate.id] }, + }); + await user.click(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => + expect(updateProject).toHaveBeenCalledWith( + MockChatProject.organization_id, + MockChatProject.id, + { + templates: { + template_ids: [dockerTemplate.id, kubernetesTemplate.id], + }, + }, + ), + ); + }); +}); diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx index f15b0149f8b..495cb768390 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx @@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import { chatProjectInstructions, deleteChatProjectInstructions, + updateChatProject, updateChatProjectInstructions, } from "#/api/queries/chatProjects"; import { user } from "#/api/queries/users"; @@ -11,12 +12,13 @@ import { useAuthenticated } from "#/hooks/useAuthenticated"; import { useDashboard } from "#/modules/dashboard/useDashboard"; import { ProjectDetailsPanelView } from "./ProjectDetailsPanelView"; import { ProjectInstructionsDialog } from "./ProjectInstructionsDialog"; +import { ProjectTemplatesDialog } from "./ProjectTemplatesDialog"; type ProjectDetailsPanelProps = { readonly project: ChatProject; }; -/** Loads a project's details and edits its instructions. */ +/** Loads a project's details and edits its templates and instructions. */ export const ProjectDetailsPanel: React.FC = ({ project, }) => ( @@ -56,7 +58,9 @@ const ProjectDetailsPanelContent: React.FC = ({ const deleteMutation = useMutation( deleteChatProjectInstructions(queryClient, project), ); + const updateTemplatesMutation = useMutation(updateChatProject(queryClient)); const [isEditorOpen, setIsEditorOpen] = useState(false); + const [isTemplatesEditorOpen, setIsTemplatesEditorOpen] = useState(false); // isPending reaches the dialog only after a render, so a fast double // click could otherwise submit the same change twice. const isMutationInFlight = useRef(false); @@ -89,11 +93,29 @@ const ProjectDetailsPanelContent: React.FC = ({ ownerLabel={ownerLabel} createdAt={project.created_at} organizationLabel={organizationLabel} + templateCount={project.templates.template_ids.length} + onEditTemplates={() => { + updateTemplatesMutation.reset(); + setIsTemplatesEditorOpen(true); + }} instructions={instructionsQuery.data} error={instructionsQuery.error} onRetry={() => void instructionsQuery.refetch()} onEditInstructions={openEditor} /> + + updateTemplatesMutation.mutate( + { project, request: { templates } }, + { onSuccess: () => setIsTemplatesEditorOpen(false) }, + ) + } + /> = { args: { ownerLabel: "You", createdAt: MockChatProject.created_at, + templateCount: 2, + onEditTemplates: fn(), instructions: MockChatProjectInstructions, error: undefined, onRetry: fn(), @@ -38,6 +40,14 @@ export const WithOrganization: Story = { args: { organizationLabel: "Coder" }, }; +export const NoTemplates: Story = { + args: { templateCount: 0 }, +}; + +export const OneTemplate: Story = { + args: { templateCount: 1 }, +}; + export const OwnerLoading: Story = { args: { ownerLabel: undefined }, }; diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx index fb1185e0c69..86983121431 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx @@ -3,6 +3,7 @@ import { useId } from "react"; import type { ChatProjectInstructions } from "#/api/typesGenerated"; import { ErrorAlert } from "#/components/Alert/ErrorAlert"; import { Button } from "#/components/Button/Button"; +import { Link } from "#/components/Link/Link"; import { Skeleton } from "#/components/Skeleton/Skeleton"; import { Tooltip, @@ -17,6 +18,10 @@ type ProjectDetailsPanelViewProps = { readonly createdAt: string; /** Omit to hide the organization row. */ readonly organizationLabel?: string; + /** The number of templates the project's chats may use. */ + readonly templateCount: number; + /** Opens the editor for the project's templates. */ + readonly onEditTemplates: () => void; /** `undefined` until the instructions load. */ readonly instructions: ChatProjectInstructions | undefined; readonly error: unknown; @@ -32,6 +37,8 @@ export const ProjectDetailsPanelView: React.FC< ownerLabel, createdAt, organizationLabel, + templateCount, + onEditTemplates, instructions, error, onRetry, @@ -87,6 +94,21 @@ export const ProjectDetailsPanelView: React.FC< {organizationLabel !== undefined && ( {organizationLabel} )} + + + + +
= { + title: "pages/AgentsPage/ProjectPage/ProjectTemplatesDialog", + component: ProjectTemplatesDialog, + args: { + project: MockChatProject, + open: true, + onOpenChange: fn(), + isSaving: false, + error: undefined, + onSave: fn(), + }, + parameters: { + queries: [templatesQuery], + }, +}; + +export default meta; +type Story = StoryObj; + +export const NoTemplates: Story = {}; + +export const DefaultPreset: Story = { + args: { project: projectWithDefaultPreset }, + parameters: { + queries: [ + templatesQuery, + { + key: templateVersionPresetsKey(dockerTemplate.active_version_id), + data: [smallPreset], + }, + ], + }, +}; + +export const DefaultParameters: Story = { + args: { project: projectWithDefaultParameters }, + parameters: { + queries: [ + templatesQuery, + { + key: richParametersKey(dockerTemplate.active_version_id), + data: [MockTemplateVersionParameter1, MockTemplateVersionParameter2], + }, + ], + }, +}; + +export const Saving: Story = { + args: { ...DefaultPreset.args, isSaving: true }, + parameters: DefaultPreset.parameters, +}; + +export const SaveError: Story = { + args: { + project: projectWithDefaultParameters, + error: mockApiError({ + message: + 'Parameter "cpu" has an invalid value: value 99 is more than the maximum 8', + validations: [ + { + field: "templates.default_parameters", + detail: + 'Parameter "cpu" has an invalid value: value 99 is more than the maximum 8', + }, + ], + }), + }, + parameters: DefaultParameters.parameters, +}; + +export const RequestError: Story = { + args: { + project: projectWithDefaultPreset, + error: mockApiError({ message: "Failed to update chat project." }), + }, + parameters: DefaultPreset.parameters, +}; diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.tsx new file mode 100644 index 00000000000..1ea90e23bcc --- /dev/null +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.tsx @@ -0,0 +1,121 @@ +import { useState } from "react"; +import { useQuery } from "react-query"; +import { isApiValidationError } from "#/api/errors"; +import { agentTemplates } from "#/api/queries/templates"; +import type { ChatProject, ChatProjectTemplates } from "#/api/typesGenerated"; +import { ErrorAlert } from "#/components/Alert/ErrorAlert"; +import { Button } from "#/components/Button/Button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "#/components/Dialog/Dialog"; +import { Spinner } from "#/components/Spinner/Spinner"; +import { + ChatProjectTemplatesFields, + chatProjectTemplatesRequest, + chatProjectTemplatesValues, + templatesValidationDetail, +} from "../ChatsSidebar/dialogs/ChatProjectTemplatesFields"; + +type ProjectTemplatesDialogProps = { + readonly project: ChatProject; + readonly open: boolean; + readonly onOpenChange: (open: boolean) => void; + readonly isSaving: boolean; + readonly error: unknown; + readonly onSave: (templates: ChatProjectTemplates) => void; +}; + +/** Edits the templates a project's chats may use, and their defaults. */ +export const ProjectTemplatesDialog: React.FC = ({ + open, + onOpenChange, + ...formProps +}) => { + const handleOpenChange = (nextOpen: boolean) => { + if (!nextOpen && !formProps.isSaving) { + onOpenChange(false); + } + }; + + return ( + + + handleOpenChange(false)} + /> + + + ); +}; + +type ProjectTemplatesFormProps = Omit< + ProjectTemplatesDialogProps, + "open" | "onOpenChange" +> & { + readonly onCancel: () => void; +}; + +const ProjectTemplatesForm: React.FC = ({ + project, + isSaving, + error, + onSave, + onCancel, +}) => { + // The dialog content mounts on open, so the draft starts from the + // templates saved at that time and a project refetch does not reset it. + const [initialValue] = useState(() => + chatProjectTemplatesValues(project.templates), + ); + const [value, setValue] = useState(initialValue); + const templatesQuery = useQuery(agentTemplates(project.organization_id)); + const eligibleTemplates = templatesQuery.data; + const isUnchanged = JSON.stringify(value) === JSON.stringify(initialValue); + const canSave = eligibleTemplates !== undefined && !isUnchanged && !isSaving; + + return ( +
{ + event.preventDefault(); + if (canSave) { + onSave(chatProjectTemplatesRequest(value, eligibleTemplates)); + } + }} + > + + Edit templates + + + {Boolean(error) && !isApiValidationError(error) && ( + + )} + + + + + + ); +}; From 1bf80b1d6b5341d48ed547fada8376c66e2f1c6a Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 16 Oct 2026 13:26:57 +0000 Subject: [PATCH 2/3] chore(site/src/api/queries): export templateVersionPresetsKey for the templates dialog stories --- site/src/api/queries/templates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/api/queries/templates.ts b/site/src/api/queries/templates.ts index 180aaf4ee04..2c11b677779 100644 --- a/site/src/api/queries/templates.ts +++ b/site/src/api/queries/templates.ts @@ -381,7 +381,7 @@ export const previousTemplateVersion = ( }; }; -const templateVersionPresetsKey = (versionId: string) => [ +export const templateVersionPresetsKey = (versionId: string) => [ templateVersionRoot, versionId, "presets", From b5c4fbbde0f661cac1665b3a0c4d30de61891b53 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 16 Oct 2026 13:42:11 +0000 Subject: [PATCH 3/3] fix(site/src/pages/AgentsPage/components/ProjectPage): send one templates update per save and cover the loading state --- .../ProjectPage/ProjectDetailsPanel.test.tsx | 16 ++++++++++ .../ProjectPage/ProjectDetailsPanel.tsx | 9 ++++-- .../ProjectPage/ProjectDetailsPanelView.tsx | 2 -- .../ProjectTemplatesDialog.stories.tsx | 31 ++++++++++++++----- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx index b4071b90252..80755e20368 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.test.tsx @@ -526,6 +526,22 @@ describe("ProjectDetailsPanel templates", () => { ); }); + it("sends one templates update when Save is double clicked", async () => { + const { user, updateProject } = setup(); + updateProject.mockReturnValue(new Promise(() => {})); + + await user.click( + screen.getByRole("button", { name: "Edit templates: 0 templates" }), + ); + await user.click(await screen.findByRole("button", { name: "Select all" })); + await userEvent + .setup({ delay: null }) + .dblClick(screen.getByRole("button", { name: "Save" })); + + await waitFor(() => expect(updateProject).toHaveBeenCalled()); + expect(updateProject).toHaveBeenCalledTimes(1); + }); + it("starts the editor from the saved templates", async () => { const { user, updateProject, rerenderWithProject } = setup(); rerenderWithProject({ diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx index 495cb768390..ead6bf284d6 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanel.tsx @@ -68,6 +68,7 @@ const ProjectDetailsPanelContent: React.FC = ({ const resetErrors = () => { updateMutation.reset(); deleteMutation.reset(); + updateTemplatesMutation.reset(); }; const openEditor = () => { @@ -110,9 +111,11 @@ const ProjectDetailsPanelContent: React.FC = ({ isSaving={updateTemplatesMutation.isPending} error={updateTemplatesMutation.error} onSave={(templates) => - updateTemplatesMutation.mutate( - { project, request: { templates } }, - { onSuccess: () => setIsTemplatesEditorOpen(false) }, + runMutation((onSettled) => + updateTemplatesMutation.mutate( + { project, request: { templates } }, + { onSuccess: () => setIsTemplatesEditorOpen(false), onSettled }, + ), ) } /> diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx index 86983121431..610f49ef151 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectDetailsPanelView.tsx @@ -18,9 +18,7 @@ type ProjectDetailsPanelViewProps = { readonly createdAt: string; /** Omit to hide the organization row. */ readonly organizationLabel?: string; - /** The number of templates the project's chats may use. */ readonly templateCount: number; - /** Opens the editor for the project's templates. */ readonly onEditTemplates: () => void; /** `undefined` until the instructions load. */ readonly instructions: ChatProjectInstructions | undefined; diff --git a/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.stories.tsx b/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.stories.tsx index 526b6d1c65c..c36e19ec66c 100644 --- a/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.stories.tsx +++ b/site/src/pages/AgentsPage/components/ProjectPage/ProjectTemplatesDialog.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { fn } from "storybook/test"; +import { fn, spyOn } from "storybook/test"; +import { API } from "#/api/api"; import { agentTemplates, richParametersKey, @@ -54,10 +55,6 @@ const projectWithDefaultParameters: ChatProject = { default_parameters: { [MockTemplateVersionParameter1.name]: "custom" }, }, }; -const templatesQuery = { - key: agentTemplates(MockChatProject.organization_id).queryKey, - data: [dockerTemplate, kubernetesTemplate], -}; const meta: Meta = { title: "pages/AgentsPage/ProjectPage/ProjectTemplatesDialog", @@ -71,7 +68,12 @@ const meta: Meta = { onSave: fn(), }, parameters: { - queries: [templatesQuery], + queries: [ + { + key: agentTemplates(MockChatProject.organization_id).queryKey, + data: [dockerTemplate, kubernetesTemplate], + }, + ], }, }; @@ -80,11 +82,21 @@ type Story = StoryObj; export const NoTemplates: Story = {}; +export const Loading: Story = { + parameters: { queries: [] }, + beforeEach: () => { + spyOn(API, "getTemplates").mockReturnValue(new Promise(() => {})); + }, +}; + export const DefaultPreset: Story = { args: { project: projectWithDefaultPreset }, parameters: { queries: [ - templatesQuery, + { + key: agentTemplates(MockChatProject.organization_id).queryKey, + data: [dockerTemplate, kubernetesTemplate], + }, { key: templateVersionPresetsKey(dockerTemplate.active_version_id), data: [smallPreset], @@ -97,7 +109,10 @@ export const DefaultParameters: Story = { args: { project: projectWithDefaultParameters }, parameters: { queries: [ - templatesQuery, + { + key: agentTemplates(MockChatProject.organization_id).queryKey, + data: [dockerTemplate, kubernetesTemplate], + }, { key: richParametersKey(dockerTemplate.active_version_id), data: [MockTemplateVersionParameter1, MockTemplateVersionParameter2],