SearchCtrlK

  • ↑↓navigate
  • ↵open
  • escclose

Can’t find it? Contact us

MergeBox

The box at the end of a pull request that answers "can I merge, and if not, why?".

1 approving reviewana approved these changes
All checks have passed4 successful checks
4 successful checks
No conflicts with the base branch
Choose a merge method
View code
tsx
<MergeBox
	state="ready"
	action="/acme/delta/pull/412/merge"
	rows={{
		reviews: { tone: "success", title: "1 approving review", detail: "ana approved these changes" },
		checks: {
			tone: "success",
			title: "All checks have passed",
			detail: "4 successful checks",
			children: <CheckRuns runs={runs} labels={checkLabels} />,
		},
		conflicts: { tone: "success", title: "No conflicts with the base branch" },
	}}
	labels={{
		methods: { merge: "Merge pull request", squash: "Squash and merge", rebase: "Rebase and merge" },
		methodDetails: {
			merge: "All commits from this branch will be added to the base branch",
			squash: "The 6 commits from this branch will be combined into one",
			rebase: "The 6 commits from this branch will be rebased onto the base branch",
		},
		chooseMethod: "Choose a merge method",
		status: {
			blocked: "Merging is blocked until a reviewer approves",
			conflicts: "Resolve the conflicts before merging",
			merged: "Pull request merged into main",
			closed: "Pull request closed without merging",
		},
	}}
/>

Demo apps

Whole pages built only from @sdxc/ui, each for an invented product with its own palette and type. They open in a new tab.

Every demo, landings and docs sites alike, is listed in the demo gallery.

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

Terminal
npm add @sdxc/ui remix
typescript
import "@sdxc/ui/theme.css";

Usage

typescript
import { MergeBox } from "@sdxc/ui/app";

Renders the precondition rows, then the merge control or the outcome. While the request can't merge, both halves of the split button stay on screen, disabled, and the sentence naming why is their aria-describedby. The method menu is a native popover of radios posting method, and the button names the chosen method through CSS, so the box needs no island at all.

Examples

Blocked on a review

Review requiredAt least 1 approving review is required by reviewers with write access
Some checks haven't completed yet1 pending and 3 successful checks
No conflicts with the base branch
Choose a merge method

Merging is blocked until a reviewer approves

View code
tsx
<MergeBox
	state="blocked"
	action="/acme/delta/pull/418/merge"
	rows={{
		reviews: {
			tone: "warning",
			title: "Review required",
			detail: "At least 1 approving review is required by reviewers with write access",
		},
		checks: { tone: "warning", title: "Some checks haven't completed yet", detail: "1 pending and 3 successful checks" },
		conflicts: { tone: "success", title: "No conflicts with the base branch" },
	}}
	labels={labels}
/>

Conflicts with the base branch

2 approving reviews
1 failing checktest (ubuntu-latest) failed after 3m 41s
This branch has conflicts that must be resolvedConflicting files: src/parser.ts, src/lexer/tokens.ts
Choose a merge method

Resolve the conflicts before merging

View code
tsx
<MergeBox
	state="conflicts"
	action="/acme/delta/pull/405/merge"
	rows={{
		reviews: { tone: "success", title: "2 approving reviews" },
		checks: { tone: "danger", title: "1 failing check", detail: "test (ubuntu-latest) failed after 3m 41s" },
		conflicts: {
			tone: "danger",
			title: "This branch has conflicts that must be resolved",
			detail: "Conflicting files: src/parser.ts, src/lexer/tokens.ts",
		},
	}}
	labels={labels}
/>

Merged, with the branch left to delete

Pull request merged into main

View code
tsx
<MergeBox
	state="merged"
	action="/acme/delta/pull/399/merge"
	labels={labels}
	followUp={
		<form method="post" action="/acme/delta/branches/fix-tokenizer-offsets/delete">
			<Button type="submit" variant="outline" size="sm">Delete branch</Button>
		</form>
	}
/>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

PropTypeRequiredDescription
state"ready" | "blocked" | "conflicts" | "merged" | "closed"YesEnables the merge button only at ready, and swaps it for the outcome once settled.
rowsRowsNoThe preconditions shown above the button; an omitted row is left out.
methodsMethod[]NoThe methods offered. @default ["merge", "squash", "rebase"]
defaultMethod"merge" | "squash" | "rebase"NoThe method chosen when the box renders. Defaults to the first of methods.
actionstringYesWhere the form posts; the body carries method and any fields.
fieldsRemixNodeNoExtra hidden fields posted with the merge, such as a CSRF token.
followUpRemixNodeNoWhat follows a settled pull request, such as a "Delete branch" form.
labelsLabelsYesEvery string the box shows, so it reads in any language.

Also accepts everything in Omit<TagProps<"section">, "children">.

Types

NameValuesWhat it decides
State"ready" | "blocked" | "conflicts" | "merged" | "closed"Where the pull request stands: ready can merge, blocked waits on a review or a check, conflicts needs the branch updated, and merged and closed are settled.
Method"merge" | "squash" | "rebase"How the branch lands: a merge commit, one squashed commit, or rebased commits.
Tone"success" | "warning" | "danger" | "neutral"The tone a row reads in: met is success, waiting warning, failing danger.
@sdxc/ui

Can’t find it? Contact us