Clarify workflow step conditions

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-13 22:41:31 -07:00
parent 314cfd0b7b
commit 5cdbf19fd1
4 changed files with 53 additions and 7 deletions
@@ -179,9 +179,7 @@ export function WorkflowConditionBuilder({
return (
<div className="space-y-3">
<fieldset aria-label="Condition">
<legend className="mb-2 text-sm font-medium text-foreground">
Run when
</legend>
<legend className="sr-only">Condition</legend>
<div className="grid grid-cols-2 gap-2.5">
{fieldOptions.map((field) => {
const isMatchAll = field.value === "";
@@ -6,6 +6,7 @@ import { Button } from "@/shared/ui/button";
import { Input } from "@/shared/ui/input";
import { Textarea } from "@/shared/ui/textarea";
import { ChannelCombobox } from "./ChannelCombobox";
import { WorkflowConditionBuilder } from "./WorkflowConditionBuilder";
import { WorkflowEmojiField } from "./WorkflowEmojiField";
import { WorkflowTemplateTextarea } from "./WorkflowTemplateTextarea";
import { FieldLabel, FormSelect } from "./workflowFormPrimitives";
@@ -378,6 +379,23 @@ export function WorkflowStepCard({
/>
</section>
<section className="space-y-4 border-t border-border/50 py-5">
<SectionHeading title="Step condition" />
<p className="text-xs text-muted-foreground">
Checked after the workflow starts. If it does not match, Buzz skips
only this step and continues the run.
</p>
<WorkflowConditionBuilder
channels={channels}
disabled={disabled}
idPrefix={`${prefix}-condition`}
matchAllLabel="Always run this step"
onChange={(condition) => onUpdate({ ...step, condition })}
triggerType={triggerType}
value={step.condition ?? ""}
/>
</section>
<section className="space-y-4 border-t border-border/50 py-5">
<SectionHeading title="Step timeout" />
<div className="space-y-1.5">
@@ -63,7 +63,7 @@ const FIELDS_BY_TRIGGER: Record<TriggerType, ConditionField[]> = {
reaction_added: [
{ label: "Reaction emoji", value: "trigger_emoji" },
AUTHOR_FIELD,
{ label: "Reacted-to message ID", value: "trigger_message_id" },
{ label: "Message ID", value: "trigger_message_id" },
],
webhook: [{ label: "Webhook field…", value: "webhook_field" }],
schedule: [{ label: "Scheduled timestamp", value: "trigger_timestamp" }],
+33 -3
View File
@@ -277,7 +277,6 @@ test("builds a valid trigger condition from plain-language choices", async ({
const inspector = dialog.getByTestId("workflow-node-inspector");
const conditionFields = inspector.getByRole("group", { name: "Condition" });
await expect(inspector.getByText("Run when", { exact: true })).toBeVisible();
const conditionOptions = conditionFields.getByRole("button");
await expect(conditionOptions).toHaveCount(4);
const allMessages = conditionFields.getByRole("button", {
@@ -330,6 +329,30 @@ test("builds a valid trigger condition from plain-language choices", async ({
await expect(inspector.getByLabel("Custom expression")).not.toBeVisible();
});
test("keeps a step condition separate from its action configuration", async ({
page,
}) => {
await navigateToWorkflows(page);
await page.getByRole("button", { name: "Create Workflow" }).click();
const dialog = page.getByRole("dialog");
await dialog.getByRole("button", { name: "Add step" }).click();
await page.getByRole("menuitem", { name: "Send Message" }).click();
const inspector = dialog.getByTestId("workflow-node-inspector");
await expect(inspector.getByLabel("Message text")).toBeVisible();
await expect(
inspector.getByRole("heading", { name: "Step condition" }),
).toBeVisible();
await inspector.getByRole("button", { name: "Message text" }).click();
await inspector.getByLabel("Text to match").fill("deploy");
await dialog.getByRole("tab", { name: "YAML" }).click();
await expect(dialog.getByLabel("Workflow YAML")).toHaveValue(
/if: str_contains\(trigger_text, "deploy"\)/,
);
});
test("chooses a trigger author condition from live user search", async ({
page,
}) => {
@@ -505,7 +528,7 @@ test("chooses a reaction emoji condition with the app emoji picker", async ({
inspector = dialog.getByTestId("workflow-node-inspector");
await expect(
inspector.getByRole("button", { name: "Reacted-to message ID" }),
inspector.getByRole("button", { name: "Message ID" }),
).toBeVisible();
await expect(
inspector.getByRole("button", { name: "Channel ID" }),
@@ -633,8 +656,15 @@ test("opens node configuration in a contextual inspector", async ({ page }) => {
"send_message",
);
await expect(inspector.getByLabel("Message text")).toBeVisible();
await expect(
inspector.getByRole("heading", { name: "Step condition" }),
).toBeVisible();
await expect(
inspector.getByRole("button", { name: "Always run this step" }),
).toHaveAttribute("aria-pressed", "true");
await expect(inspector.getByText(/skips only this step/)).toBeVisible();
await expect(inspector.getByRole("group", { name: "Condition" })).toHaveCount(
0,
1,
);
await expect(
inspector.getByRole("heading", { name: "Step timeout" }),