From 366afbb4a7959365be8de2dccfc2e38442ae09ed Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Fri, 18 Sep 2026 16:25:31 -0700 Subject: [PATCH 1/4] refactor(emcn): centralize compact icon button sizing --- .../components/find-bar/find-bar.tsx | 12 ++-- .../browser-session/browser-find-bar.tsx | 9 ++- .../workflow-sidebar/workflow-sidebar.tsx | 3 +- .../components/action-bar/action-bar.tsx | 9 ++- .../components/file-upload/file-upload.tsx | 6 +- .../messages-input/messages-input.tsx | 12 ++-- .../selector-combobox/selector-combobox.tsx | 3 +- .../panel/components/editor/editor.tsx | 3 +- .../preview-editor/preview-editor.tsx | 3 +- .../src/components/button/button.test.tsx | 66 +++++++++++++++++++ .../emcn/src/components/button/button.tsx | 17 ++++- .../src/note/note-block-view.tsx | 3 +- 12 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 packages/emcn/src/components/button/button.test.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/components/find-bar/find-bar.tsx b/apps/sim/app/workspace/[workspaceId]/components/find-bar/find-bar.tsx index ceb2b0b709b..6ec4de4377e 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/find-bar/find-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/find-bar/find-bar.tsx @@ -129,7 +129,8 @@ export const FindBar = memo(function FindBar({ type='button' variant='ghost' size='icon' - className='size-6 shrink-0' + iconSize='compact' + className='shrink-0' aria-label={showReplace ? 'Hide replace' : 'Show replace'} aria-expanded={showReplace} onClick={() => setShowReplace((visible) => !visible)} @@ -179,7 +180,8 @@ export const FindBar = memo(function FindBar({ type='button' variant='ghost' size='icon' - className='size-6 shrink-0' + iconSize='compact' + className='shrink-0' aria-label='Previous match' title='Previous match (Shift+Enter)' disabled={!navEnabled} @@ -191,7 +193,8 @@ export const FindBar = memo(function FindBar({ type='button' variant='ghost' size='icon' - className='size-6 shrink-0' + iconSize='compact' + className='shrink-0' aria-label='Next match' title='Next match (Enter)' disabled={!navEnabled} @@ -203,7 +206,8 @@ export const FindBar = memo(function FindBar({ type='button' variant='ghost' size='icon' - className='size-6 shrink-0' + iconSize='compact' + className='shrink-0' aria-label='Close find' title='Close (Esc)' onClick={onClose} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx index a3f9e23198d..8313c2c549e 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx @@ -106,7 +106,8 @@ export function BrowserFindBar({ inputRef, onClose, scopeId }: BrowserFindBarPro size='sm' aria-label='Previous match' disabled={!result?.matches} - className='size-[24px] shrink-0 p-0' + iconSize='compact-fixed' + className='shrink-0' onClick={() => step('back')} > @@ -117,7 +118,8 @@ export function BrowserFindBar({ inputRef, onClose, scopeId }: BrowserFindBarPro size='sm' aria-label='Next match' disabled={!result?.matches} - className='size-[24px] shrink-0 p-0' + iconSize='compact-fixed' + className='shrink-0' onClick={() => step('forward')} > @@ -127,7 +129,8 @@ export function BrowserFindBar({ inputRef, onClose, scopeId }: BrowserFindBarPro variant='ghost-secondary' size='sm' aria-label='Close find bar' - className='size-[24px] shrink-0 p-0' + iconSize='compact-fixed' + className='shrink-0' onClick={dismiss} > diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx index 18b04cfd5b2..2c53382bf54 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx @@ -730,7 +730,8 @@ export function WorkflowSidebarBody({ 'noopener,noreferrer' ) } - className='absolute right-[6px] bottom-1.5 z-10 size-[24px] cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] p-0 hover-hover:bg-[var(--surface-4)]' + iconSize='compact-fixed' + className='absolute right-[6px] bottom-1.5 z-10 cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] hover-hover:bg-[var(--surface-4)]' > diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx index c42c236d1c3..4bd3f1c6c00 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx @@ -36,7 +36,7 @@ import { useWorkflowStore } from '@/stores/workflows/workflow/store' const DEFAULT_DUPLICATE_OFFSET = { x: 50, y: 50 } const ACTION_BUTTON_STYLES = [ - 'size-[24px] rounded-md p-0', + 'rounded-md', 'border-none bg-transparent text-[var(--text-icon)]', 'hover-hover:bg-[var(--surface-5)] hover-hover:text-[var(--text-primary)]!', 'dark:hover-hover:bg-[var(--surface-4)]', @@ -472,6 +472,7 @@ export const ActionBar = memo( } if (canRunBlock) handleRunFromBlockClick() }} + iconSize='compact-fixed' className={cn(getActionButtonStyles('run'), isWorkflowRunning && 'group/run')} disabled={!canStopWorkflow && !canRunBlock} > @@ -517,6 +518,7 @@ export const ActionBar = memo( collaborativeBatchToggleBlockEnabled([blockId]) } }} + iconSize='compact-fixed' className={getActionButtonStyles('enabled')} disabled={ isWorkflowRunning || @@ -553,6 +555,7 @@ export const ActionBar = memo( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx index 6a1ce331611..a583115eaa6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx @@ -1398,7 +1398,8 @@ function PreviewEditorContent({ type='button' variant='ghost' onClick={handleExpandChildWorkflow} - className='absolute right-[6px] bottom-1.5 z-10 size-[24px] cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] p-0 hover-hover:bg-[var(--surface-4)]' + iconSize='compact-fixed' + className='absolute right-[6px] bottom-1.5 z-10 cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] hover-hover:bg-[var(--surface-4)]' > {isExecutionMode && onDrillDown ? ( diff --git a/packages/emcn/src/components/button/button.test.tsx b/packages/emcn/src/components/button/button.test.tsx new file mode 100644 index 00000000000..154820a5b0b --- /dev/null +++ b/packages/emcn/src/components/button/button.test.tsx @@ -0,0 +1,66 @@ +/** @vitest-environment node */ +import { Button } from '@sim/emcn' +import { renderToStaticMarkup } from 'react-dom/server' +import { describe, expect, it } from 'vitest' + +function normalizeClasses(markup: string) { + return markup.replace( + /class="([^"]+)"/g, + (_, classes: string) => `class="${classes.split(/\s+/).sort().join(' ')}"` + ) +} + +/** The three existing Button treatments used by compact product actions. */ +const TREATMENTS = [ + { size: undefined, variant: 'ghost' }, + { size: 'sm', variant: 'ghost-secondary' }, + { size: 'icon', variant: 'ghost' }, +] as const + +describe('Button iconSize', () => { + for (const [iconSize, previousClass] of [ + ['compact', 'size-6 p-0'], + ['compact-fixed', 'size-[24px] p-0'], + ] as const) { + it.each(TREATMENTS)( + `preserves the ${iconSize} treatment with size=$size and variant=$variant`, + (treatment) => { + const icon = + const before = renderToStaticMarkup( + + ) + const after = renderToStaticMarkup( + + ) + expect(normalizeClasses(after)).toBe(normalizeClasses(before)) + } + ) + } + + it('allows explicit padding and consumer width to take precedence', () => { + const markup = renderToStaticMarkup( + + */ + iconSize?: VariantProps['iconSize'] /** * Symmetric padding for icon actions whose content or layout determines their size. * Preserves the selected size's typography, corner radius and icon stroke. @@ -85,11 +98,11 @@ export interface ButtonProps } const Button = forwardRef( - ({ className, variant, size, iconPadding, ...props }, ref) => { + ({ className, variant, size, iconSize, iconPadding, ...props }, ref) => { return (