From f6526b789a77c9c0eccef9874a9e505cb0158674 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 17 Oct 2025 18:11:31 +0100 Subject: [PATCH] feat(site): make TaskPrompt PromptTextarea read-only when submitting (#20363) - Makes the text area of TaskPrompt read-only when submitting - Adds a "scanning" animation to the textarea on submit. --- .../tasks/TaskPrompt/TaskPrompt.stories.tsx | 29 +++++++++++++ .../modules/tasks/TaskPrompt/TaskPrompt.tsx | 41 ++++++++++++++----- site/tailwind.config.js | 8 ++++ 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx index 3c7025fa76..bac6c41aab 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx @@ -103,6 +103,35 @@ export const SubmitDisabledWhenPromptEmpty: Story = { }, }; +export const Submitting: Story = { + decorators: [withGlobalSnackbar], + beforeEach: () => { + spyOn(API.experimental, "createTask").mockImplementation( + () => + // Never resolve to keep the component in the submitting state for visual testing. + new Promise(() => {}), + ); + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const prompt = await canvas.findByLabelText(/prompt/i); + await userEvent.type( + prompt, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.{enter}{enter}Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + ); + + const submitButton = canvas.getByRole("button", { name: /run task/i }); + await waitFor(() => expect(submitButton).toBeEnabled()); + await userEvent.click(submitButton); + }, + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, +}; + export const OnSuccess: Story = { decorators: [withGlobalSnackbar], parameters: { diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx index 706b3480c0..580b520d2c 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx @@ -259,6 +259,7 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { value={prompt} onChange={(e) => setPrompt(e.target.value)} readOnly={isPromptReadOnly} + isSubmitting={createTaskMutation.isPending} />
@@ -457,17 +458,35 @@ async function createTaskWithLatestTemplateVersion( }); } -const PromptTextarea: FC = (props) => { +type PromptTextareaProps = TextareaAutosizeProps & { + isSubmitting?: boolean; +}; + +const PromptTextarea: FC = ({ + isSubmitting, + ...props +}) => { return ( - +
+ + {isSubmitting && ( +
+
+
+ )} +
); }; diff --git a/site/tailwind.config.js b/site/tailwind.config.js index 1250122bb2..d7293ee2d6 100644 --- a/site/tailwind.config.js +++ b/site/tailwind.config.js @@ -83,6 +83,14 @@ module.exports = { "75%": { opacity: 0.3 }, "100%": { opacity: 0.2 }, }, + "caret-scan": { + "0%": { left: "0%" }, + "100%": { left: "100%" }, + }, + }, + animation: { + loading: "loading 2s ease-in-out infinite alternate", + "caret-scan": "caret-scan 3s ease-in-out infinite", }, }, },