feat(ui): standardize responsive control sizing (#3563)

This commit is contained in:
Carlos Florêncio
2026-09-10 18:14:05 +01:00
committed by GitHub
parent 8b64aed179
commit 5ffbda61a9
245 changed files with 2862 additions and 824 deletions
@@ -56,7 +56,7 @@ export function IntegrationAuthErrorMessage({
: cleanIntegrationErrorMessage(error)}
</span>
{isAuth && (
<Button asChild size="sm" variant="outline" className="cursor-pointer h-7 text-xs">
<Button asChild size="sm" variant="outline" className="cursor-pointer text-xs">
<Link href={reconnectHref}>{t("integrations:reconnectProvider", { name })}</Link>
</Button>
)}
@@ -46,9 +46,9 @@ export function ChangeRequestDetailCopyButton({
<TooltipTrigger asChild>
<Button
type="button"
size="sm"
size="icon"
variant="ghost"
className="h-6 w-6 min-h-11 min-w-11 cursor-pointer p-0 text-muted-foreground hover:text-foreground [@media(min-width:640px)_and_(pointer:fine)]:min-h-0 [@media(min-width:640px)_and_(pointer:fine)]:min-w-0"
className="cursor-pointer p-0 text-muted-foreground hover:text-foreground"
onClick={() => {
void copyToClipboard(url).then((success) => {
if (success) setCopied(true);
@@ -53,9 +53,8 @@ export function ChangeRequestActionButton({
return (
<Button
type={type}
size="sm"
variant={actionVariant(action.tone)}
className={`min-h-11 cursor-pointer sm:min-h-0 ${successClass}`}
className={`cursor-pointer ${successClass}`}
disabled={action.disabled || busy}
onClick={onAction}
aria-label={ariaLabel}
@@ -182,19 +181,15 @@ export function ChangeRequestDetailHeader({
onAction={() => void props.onAction?.({ actionId: action.id })}
/>
))}
{props.headerActions ? (
<div className="contents [&_button]:min-h-11 sm:[&_button]:min-h-0">
{props.headerActions}
</div>
) : null}
{props.headerActions ? <div className="contents">{props.headerActions}</div> : null}
{props.onRefresh ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
size="sm"
variant="ghost"
className="h-11 w-11 cursor-pointer p-0 text-muted-foreground sm:h-6 sm:w-6"
size="icon"
className="cursor-pointer p-0 text-muted-foreground"
onClick={props.onRefresh}
disabled={props.loading}
aria-label={t("integrations:refresh")}
@@ -272,7 +272,9 @@ describe("ChangeRequestDetail actions and states", () => {
});
const approve = screen.getByRole("button", { name: "Approve" });
expect(approve.classList.contains("min-h-11")).toBe(true);
expect(approve.classList.contains("h-7")).toBe(true);
expect(approve.classList.contains("max-md:h-11")).toBe(true);
expect(approve.classList.contains("[@media(pointer:coarse)]:h-11")).toBe(true);
fireEvent.click(approve);
expect(onAction).toHaveBeenCalledWith({ actionId: "approve" });
@@ -350,6 +352,9 @@ describe("ChangeRequestDetail actions and states", () => {
</TooltipProvider>,
);
expect(screen.getByRole("alert").textContent).toContain("Provider unavailable");
expect(screen.getByRole("button", { name: "Retry" }).classList.contains("min-h-11")).toBe(true);
const retry = screen.getByRole("button", { name: "Retry" });
expect(retry.classList.contains("h-7")).toBe(true);
expect(retry.classList.contains("max-md:h-11")).toBe(true);
expect(retry.classList.contains("[@media(pointer:coarse)]:h-11")).toBe(true);
});
});
@@ -129,7 +129,7 @@ function DetailState({ props }: { props: ChangeRequestDetailProps }) {
>
<p>{props.error}</p>
{props.onRetry ? (
<Button type="button" variant="outline" className="min-h-11" onClick={props.onRetry}>
<Button type="button" variant="outline" onClick={props.onRetry}>
{t("integrations:retry")}
</Button>
) : null}
@@ -53,10 +53,7 @@ export function IntegrationCursorPagination({
if (!previousDisabled) onPrevious();
}}
aria-disabled={previousDisabled}
className={cn(
"min-h-11 cursor-pointer sm:min-h-9",
previousDisabled && "pointer-events-none opacity-50",
)}
className={cn("cursor-pointer", previousDisabled && "pointer-events-none opacity-50")}
/>
</PaginationItem>
<PaginationItem>
@@ -67,10 +64,7 @@ export function IntegrationCursorPagination({
if (!nextDisabled) onNext();
}}
aria-disabled={nextDisabled}
className={cn(
"min-h-11 cursor-pointer sm:min-h-9",
nextDisabled && "pointer-events-none opacity-50",
)}
className={cn("cursor-pointer", nextDisabled && "pointer-events-none opacity-50")}
/>
</PaginationItem>
</PaginationContent>
@@ -6,6 +6,7 @@ import { IconRefresh } from "@tabler/icons-react";
import { Button } from "@kandev/ui/button";
import { Input } from "@kandev/ui/input";
import { cn, formatRelativeTime } from "@/lib/utils";
import { controlSizingClassName } from "@kandev/ui/control-sizing";
export type IntegrationListToolbarProps = {
title: string;
@@ -48,7 +49,7 @@ function RefreshControls({
<Button
variant="ghost"
size="icon"
className="h-8 w-8 cursor-pointer"
className="cursor-pointer"
onClick={onRefresh}
disabled={loading}
title={t("github:refresh")}
@@ -114,7 +115,7 @@ export function IntegrationListToolbar({
if (dirty) onCommitCustomQuery();
}}
placeholder={queryPlaceholder}
className="h-8 pr-20"
className={controlSizingClassName("standard", "pr-20")}
data-testid={queryTestId}
/>
{dirty ? (
@@ -14,6 +14,7 @@ import { Input } from "@kandev/ui/input";
import { Label } from "@kandev/ui/label";
import { Combobox } from "@/components/combobox";
import { useTranslation } from "react-i18next";
import { controlSizingClassName } from "@kandev/ui/control-sizing";
const ALL_REPOSITORIES = "__all__";
@@ -66,7 +67,10 @@ function SavedQueryDetails({
placeholder={t("integrations:allRepos")}
searchPlaceholder={t("integrations:filterRepositories")}
emptyMessage={t("integrations:noRepositoriesFound")}
triggerClassName="h-11 border border-input bg-background px-3 py-2 text-sm hover:bg-secondary/50 md:h-9 md:py-1.5"
triggerClassName={controlSizingClassName(
"standard",
"border border-input bg-background px-3 text-sm hover:bg-secondary/50",
)}
testId="integration-save-query-repository-trigger"
dropdownTestId="integration-save-query-repository-dropdown"
/>
@@ -145,11 +149,15 @@ function SaveQueryForm({
) : null}
</div>
<DialogFooter>
<Button variant="outline" className="h-11 cursor-pointer md:h-9" onClick={onClose}>
<Button
variant="outline"
className={controlSizingClassName("standard", "cursor-pointer")}
onClick={onClose}
>
{t("common:cancel")}
</Button>
<Button
className="h-11 cursor-pointer md:h-9"
className={controlSizingClassName("standard", "cursor-pointer")}
disabled={!trimmedLabel || saving}
onClick={() => void handleSave()}
>
@@ -47,9 +47,8 @@ export function IntegrationStartTaskMenu<T extends IntegrationTaskPreset>({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="sm"
variant="outline"
className="h-11 cursor-pointer gap-1 sm:h-7"
className="cursor-pointer gap-1"
aria-label={triggerAriaLabel ?? t("github:task")}
data-testid={triggerTestId}
>
@@ -4,6 +4,7 @@ import { IconStar } from "@tabler/icons-react";
import { DropdownMenuCheckboxItem } from "@kandev/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { useTranslation } from "react-i18next";
import { controlSizingClassName } from "@kandev/ui/control-sizing";
type SavedQueryDefaultControlProps = {
label: string;
@@ -95,8 +96,8 @@ export function SavedQueryDefaultButton({
data-testid={testId}
onClick={onToggle}
className={cn(
controlSizingClassName("icon"),
"flex shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-wait disabled:opacity-50",
"h-11 w-11",
)}
>
<IconStar className={iconClassName} />
@@ -91,7 +91,6 @@ function TriggerButton({
return (
<Button
type="button"
size="sm"
variant="outline"
disabled={triggerDisabled}
className="cursor-pointer px-2 gap-1"
@@ -110,7 +109,7 @@ function TriggerButton({
size="icon"
disabled={triggerDisabled}
aria-label={triggerAriaLabel}
className="h-7 w-7 cursor-pointer hover:bg-muted/40 text-slate-400"
className="cursor-pointer hover:bg-muted/40 text-slate-400"
data-testid={triggerTestId}
{...rest}
>
@@ -157,7 +156,7 @@ function PopoverBody({
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
className="h-8 text-xs"
className="text-xs"
data-testid={testIdPrefix ? `${testIdPrefix}-input` : undefined}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
@@ -178,10 +177,9 @@ function PopoverBody({
<div className="flex justify-end">
<Button
type="button"
size="sm"
onClick={() => void onSubmit()}
disabled={loading || !value.trim() || !!submitDisabled}
className="h-7 cursor-pointer"
className="cursor-pointer"
data-testid={testIdPrefix ? `${testIdPrefix}-submit` : undefined}
>
{loading ? submittingLabel : submitLabel}