-
-
Notifications
You must be signed in to change notification settings - Fork 919
fix(ui): FormButtons now submits primary action on Enter with proper focus order #2785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the You can disable this status message by setting the WalkthroughThe FormButtons component in Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Note Free review on us!CodeRabbit is offering free reviews until Wed Dec 17 2025 to showcase some of the refinements we've made. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
…mponent (#2772) Fixed the issue where Cmd+Left Arrow was being intercepted by the TreeView component on task runs screen. Solution: Added a check in the getTreeProps keyboard handler to detect when metaKey (Cmd on macOS) is pressed with Left Arrow. When detected, the handler returns early without preventing the default browser behavior, allowing Chrome's native back navigation to work. Closes #<issue> ## ✅ Checklist - [ x] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md) - [ x] The PR title follows the convention. - [ x] I ran and tested the code works --- ## Testing Went to the task details page and confirmed that Cmd + Left Arrow will navigate back --- ## Changelog Modified TreeView.tsx to check for e.metaKey before handling Left Arrow key events When Cmd+Left is pressed, the event is no longer prevented, allowing browser default behavior --- Co-authored-by: Mihai Popescu <[email protected]>
This will speed up ice cold starts (*) for two reasons: - better compression ratio - faster decompression This is a minor release because zstd compression will now be enabled by default for all deployments. (*) ice cold starts happen when deploy images are not cached on the worker node yet. These cold start durations are highly dependent on image size and as it turns out, also the type of compression used.
…ts pages (#2775) ## ✅ Checklist - [x] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md) - [x] The PR title follows the convention. - [x] I ran and tested the code works --- ## Testing Set up the local github application, and tested its connection with trigger.dev Checked: - Change is backwards compatible - Actions/ux are uniform across the pages (empty Tasks, Deployments, project settings) - Connecting GH, Connecting Repo, disconnecting Repo, modifying settings --- ## Changelog - Create new resource route for GitHub settings management with loader and actions - Add GitHubSettingsPresenter to fetch connected repos and installations - Implement GitHubSettingsPanel component for reusable GitHub configuration UI - Refactor project settings page to use shared GitHubSettingsPanel component - Integrate GitHub connection flow into empty state onboarding for Tasks and Deployments - Add support for GitHub repo connection, disconnection, and branch tracking settings - Include redirect URL support for seamless navigation after GitHub actions - Remove duplicate GitHub connection code from project settings route --- ## Screenshots https://github.com/user-attachments/assets/8fc24699-640b-4f9e-afd8-b26edc945218 🐐 --------- Co-authored-by: James Ritchie <[email protected]>
…ce (#2776) - Add replace prop to LinkButton to use history replacement for adjacent run navigation - Preserve span and tab params when navigating between adjacent runs - Disable animations for completed spans in timeline to improve performance - Include spanId in runs list navigation for better context preservation - Direct link to task test page when filtering by single task with no runs - Fix minor styling issue with run friendlyId display padding
This PR makes our image builds deterministic and reproducible by ensuring that identical source code always produces the same image layers and image digest. This means that deployments where nothing has changed will no longer invalidate the image cache in our worker cluster nodes, thus avoid making the cold starts for runs worse. **Context** New deployments currently increase the cold start times for runs, as they generate a new image which needs to be pulled in the worker cluster where runs are executed. It happens also when the source code for the deployment has not changed due to non-deterministic steps in our build system. This addresses the latter issue by making builds reproducible. **Main changes** - Avoided baking `TRIGGER_DEPLOYMENT_ID` and `TRIGGER_DEPLOYMENT_VERSION` in the image, we now pass these via the supervisor instead. - Used `json-stable-stringify` for consistent key ordering in the files we generate for the build, e.g., `package.json`, `build.json`, `index.json`. - Removed `metafile.json` from the image contents as it is not actually used in the container. This is only relevant for the `analyze` command. - Added `SOURCE_DATE_EPOCH=0` and `rewrite-timestamp=true` to Docker builds to normalize file timestamps. - Removed some `timings` and `outputHashes` from build outputs and manifests. The builds are now reproducible for both native build server and Depot paths. This should also lead to better image layer cache reuse in general.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## [email protected] ### Minor Changes - feat(cli): deterministic image builds for deployments ([#2778](#2778)) - feat(cli): enable zstd compression for deployment images ([#2773](#2773)) ### Patch Changes - The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](#2767)) - fix(dev): stop max listeners exceeded warning messages when running more than 10 runs concurrently ([#2771](#2771)) - Upgrade @modelcontextprotocol/sdk to 1.24.3 ([#2768](#2768)) - Updated dependencies: - `@trigger.dev/[email protected]` - `@trigger.dev/[email protected]` - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Minor Changes - feat(cli): deterministic image builds for deployments ([#2778](#2778)) ### Patch Changes - The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](#2767)) ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` - `@trigger.dev/[email protected]` - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` ## @trigger.dev/[email protected] ### Patch Changes - Updated dependencies: - `@trigger.dev/[email protected]` --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…inspector views (#2781) ## Changelog - Add disableAdjacentRows prop to TaskRunsTable component to control table state encoding - Pass rootOnlyDefault prop from loader to TaskRunsTable for proper state management - Disable adjacent run navigation in schedule, waitpoint, and other inspector views - Preserve adjacent run navigation on main runs list page with rootOnly filter support
Co-authored-by: 0ski <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/webapp/app/components/primitives/FormButtons.tsx (1)
19-19: Consider removing the empty div fallback.The
<div />fallback maintains spacing withjustify-betweenwhencancelButtonis absent, but it's not semantically ideal.Alternative approach using conditional class:
return ( <div className={cn( - "flex w-full flex-row-reverse items-center justify-between border-t border-grid-bright pt-4", + "flex w-full items-center border-t border-grid-bright pt-4", + cancelButton ? "justify-between" : "justify-end", className )} > - {confirmButton} {cancelButton ? cancelButton : <div />} + {cancelButton} {confirmButton} </div> );Or use
ml-autoon the confirm button when cancel is absent.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/webapp/app/components/primitives/FormButtons.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use zod for validation in packages/core and apps/webapp
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use function declarations instead of default exports
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
apps/webapp/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Access all environment variables through the
envexport ofenv.server.tsinstead of directly accessingprocess.envin the Trigger.dev webapp
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
apps/webapp/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
apps/webapp/**/*.{ts,tsx}: When importing from@trigger.dev/corein the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
**/*.{js,ts,jsx,tsx,json,md,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier
Files:
apps/webapp/app/components/primitives/FormButtons.tsx
| "flex w-full flex-row-reverse items-center justify-between border-t border-grid-bright pt-4", | ||
| className | ||
| )} | ||
| > | ||
| {cancelButton ? cancelButton : <div />} {confirmButton} | ||
| {confirmButton} {cancelButton ? cancelButton : <div />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Visual and DOM order mismatch creates accessibility issues.
The approach of using flex-row-reverse with reordered DOM elements creates a mismatch between what sighted users see and what keyboard/screen reader users experience:
- Visual order (left to right): Cancel → Confirm
- DOM/tab order: Confirm → Cancel
- Impact: Screen reader users hear "Confirm" first, keyboard users tab to Confirm first, but sighted users see Cancel on the left
This violates WCAG 1.3.2 (Meaningful Sequence) and 2.4.3 (Focus Order), affecting approximately 48 FormButtons instances across the webapp.
Additionally, this fix assumes the buttons are type="submit". If they're type="button", pressing Enter won't trigger submission at all.
Recommended approach: Instead of reversing visual order, handle Enter key explicitly:
-export function FormButtons({
+export function FormButtons({
cancelButton,
confirmButton,
className,
}: {
cancelButton?: React.ReactNode;
confirmButton: React.ReactNode;
className?: string;
}) {
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.defaultPrevented) {
+ // Let the form handle submission naturally if buttons are in a form
+ // Or trigger confirmButton's onClick if needed
+ }
+ };
+
return (
<div
className={cn(
- "flex w-full flex-row-reverse items-center justify-between border-t border-grid-bright pt-4",
+ "flex w-full items-center justify-between border-t border-grid-bright pt-4",
className
)}
+ onKeyDown={handleKeyDown}
>
- {confirmButton} {cancelButton ? cancelButton : <div />}
+ {cancelButton ? cancelButton : <div />} {confirmButton}
</div>
);
}Or ensure the form structure properly handles submission with explicit button types and form attributes.
Verify button types and usage context:
#!/bin/bash
# Find FormButtons usage to check button types and context
# Search for FormButtons usage
echo "=== FormButtons usage patterns ==="
rg -A 10 -B 2 '<FormButtons' --type=tsx --type=ts
# Look for Button component definitions to understand button type defaults
echo -e "\n=== Button component definitions ==="
ast-grep --pattern 'export function Button($$$) { $$$ }'
ast-grep --pattern 'export const Button = $$$'
# Find common button patterns passed to FormButtons
echo -e "\n=== Checking button type attributes ==="
rg 'type="submit"' --type=tsx --type=ts -C 2
rg 'type="button"' --type=tsx --type=ts -C 2🤖 Prompt for AI Agents
In apps/webapp/app/components/primitives/FormButtons.tsx around lines 15-19, the
component uses flex-row-reverse while rendering Confirm before Cancel in the DOM
which creates a visual vs DOM/tab order mismatch; fix by removing
flex-row-reverse (or any CSS that reverses order) and reorder the JSX so the DOM
order matches visual order (render Cancel first, then Confirm), ensure the
Confirm control is type="submit" (or explicitly handle Enter key to trigger
submit) and update usages where necessary to pass correct button types; also run
the provided searches to verify existing FormButtons usages and Button component
defaults and change any incorrect type="button" instances to type="submit" or
adjust form handling accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's needed to ensure the action on pressing enter is setting new concurrency limit and not reverting it. If there is a better way of doing that suggest it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find FormButtons usage patterns, especially the concurrency dialog
echo "=== Finding FormButtons usage patterns ==="
rg -n "FormButtons" --type=tsx --type=ts -A 15 -B 5
echo -e "\n=== Looking for concurrency dialog specifically ==="
rg -n "concurrency" --type=tsx --type=ts -i | head -50
echo -e "\n=== Finding Button component definition ==="
fd -e tsx -e ts "Button.tsx" | head -10
Length of output: 352
🏁 Script executed:
#!/bin/bash
# Find FormButtons usage patterns
echo "=== Finding FormButtons usage patterns ==="
rg -n "FormButtons" -g "*.tsx" -g "*.ts" -A 15 -B 5 | head -200
echo -e "\n=== Looking for concurrency override dialog ==="
rg -n "concurrency" -g "*.tsx" -g "*.ts" -i -C 5 | grep -A 10 -B 10 -i "override"
echo -e "\n=== Finding Button component definition ==="
cat apps/webapp/app/components/primitives/Buttons.tsx 2>/dev/null || echo "Buttons.tsx not found at expected location"
# Look for common button pattern files
fd -e tsx "Button" | grep -v node_modules | head -20
Length of output: 95857
🏁 Script executed:
#!/bin/bash
# Look at the specific concurrency override dialog implementation
echo "=== Concurrency override dialog implementation ==="
rg -n "QueueOverrideConcurrencyButton" -g "*.tsx" -A 100 | head -300
echo -e "\n=== Looking at how cancel buttons are structured in other FormButtons ==="
rg -n "FormButtons" -g "*.tsx" -A 20 | grep -A 15 "cancelButton"
Length of output: 68971
@0ski, I completely understand the business requirement. The current solution works functionally, but there's a better approach that maintains proper accessibility without the visual/DOM order mismatch.
The real issue is that your form has two submit buttons ("Override limit" and "Remove override"), and browsers submit the first type="submit" button in DOM order when Enter is pressed. Here's a better solution:
Recommended Fix
Handle Enter key explicitly at the form level to always trigger the primary action:
<Form
method="post"
onSubmit={(e) => {
// Only intercept if Enter was pressed (not explicit button click)
const submitter = (e.nativeEvent as SubmitEvent).submitter;
if (!submitter && concurrencyLimit) {
// Enter was pressed without clicking a specific button
e.preventDefault();
const form = e.currentTarget;
const submitBtn = form.querySelector('button[name="action"][value="queue-override"]') as HTMLButtonElement;
if (submitBtn) {
submitBtn.click();
}
}
setIsOpen(false);
}}
className="space-y-3"
>
<input type="hidden" name="friendlyId" value={queue.id} />
<div className="space-y-2">
<label htmlFor="concurrencyLimit" className="text-sm text-text-bright">
Concurrency limit
</label>
<Input
type="number"
name="concurrencyLimit"
id="concurrencyLimit"
min="0"
max={environmentConcurrencyLimit}
value={concurrencyLimit}
onChange={(e) => setConcurrencyLimit(e.target.value)}
placeholder={currentLimit.toString()}
autoFocus
onKeyDown={(e) => {
if (e.key === 'Enter' && concurrencyLimit) {
e.preventDefault();
const form = e.currentTarget.form;
if (form) {
const submitBtn = form.querySelector('button[name="action"][value="queue-override"]') as HTMLButtonElement;
if (submitBtn) {
submitBtn.click();
}
}
}
}}
/>
</div>
<FormButtons
confirmButton={
<Button
type="submit"
name="action"
value="queue-override"
disabled={isLoading || !concurrencyLimit}
variant="primary/medium"
LeadingIcon={isLoading && <Spinner color="white" />}
shortcut={{ modifiers: ["mod"], key: "enter" }}
>
{isOverridden ? "Update override" : "Override limit"}
</Button>
}
cancelButton={
<div className="flex items-center justify-between gap-2">
{isOverridden && (
<Button
type="submit"
name="action"
value="queue-remove-override"
disabled={isLoading}
variant="danger/medium"
>
Remove override
</Button>
)}
<DialogClose asChild>
<Button variant="tertiary/medium">Cancel</Button>
</DialogClose>
</div>
}
/>
</Form>And revert the FormButtons component:
export function FormButtons({
cancelButton,
confirmButton,
className,
}: {
cancelButton?: React.ReactNode;
confirmButton: React.ReactNode;
className?: string;
}) {
return (
<div
className={cn(
"flex w-full items-center justify-between border-t border-grid-bright pt-4",
className
)}
>
{cancelButton ? cancelButton : <div />} {confirmButton}
</div>
);
}This approach:
- ✅ Keeps visual order: Cancel/Remove on left, Confirm on right
- ✅ Keeps DOM order matching visual order (better for screen readers and keyboard navigation)
- ✅ Makes Enter key submit the primary action
- ✅ Preserves all existing functionality
- ✅ No accessibility issues
The key is explicitly handling Enter in the Input field to trigger the correct button, rather than relying on implicit form submission behavior.
🧬 Code Graph Analysis Results
- apps/webapp/app/utils/cn.ts:77-79
export function cn(...inputs: ClassValue[]) {
return customTwMerge(clsx(inputs));
}
|
@copilot Coderabittai left some suggestion/comment about the solution. Incorporate its recommendation about the keypress instead of moving buttons around |
Implemented keypress handler approach as suggested. The solution now intercepts Enter key presses and programmatically clicks the "Override limit" button, avoiding the need to reorder DOM elements. This is a more targeted fix affecting only the queue concurrency form. Changes in commit 87c1700. |
|
@copilot Actually I don't like it. Let's revert to what it was, so with button ordering. But maybe let's add in a change to also change focus order, so:
|
Reverted to button ordering approach with focus order improvements. The implementation now uses:
Changes in commit b8201c3. |
Co-authored-by: 0ski <[email protected]>
Co-authored-by: 0ski <[email protected]>
b8201c3 to
86cfec8
Compare
Closes #
✅ Checklist
Testing
Manual verification that pressing Enter in forms using FormButtons now correctly submits the primary action (confirmButton) instead of any secondary actions in the cancelButton. Code review and security checks passed with no issues.
Changelog
Fixed form submission behavior in the FormButtons component to ensure the primary action is submitted when pressing Enter, while maintaining proper visual layout and focus/tab order for accessibility.
Implementation:
apps/webapp/app/components/primitives/FormButtons.tsxto reorder DOM elementsflexDirection: row-reverse)orderproperty for accessibility)Technical Details:
orderproperty to control tab orderflexDirection: row-reverseto reverse visual display from DOM orderBefore: Pressing Enter would submit the first submit button in DOM order (potentially a destructive action like "Remove override")
After: Pressing Enter submits the confirmButton (primary action) while tab order follows the visual left-to-right order
Impact: Affects all ~48 FormButtons instances globally across the webapp for consistent behavior
Screenshots
N/A - Behavioral fix only, no visual changes
💯
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.