From 63b223a52a4018429315faf50ab5b8d4f4085dec Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Sat, 19 Sep 2026 14:25:49 -0700 Subject: [PATCH 1/4] improvement(ui): share additional-field disclosures --- .../workflow-sidebar/workflow-sidebar.tsx | 24 ++----- .../panel/components/editor/editor.tsx | 34 +++++----- .../field-divider/field-divider.dom.test.tsx | 64 +++++++++++++++++++ .../field-divider/field-divider.tsx | 40 +++++++++++- packages/emcn/src/components/index.ts | 7 +- 5 files changed, 130 insertions(+), 39 deletions(-) create mode 100644 packages/emcn/src/components/field-divider/field-divider.dom.test.tsx 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 2c53382bf54..5ccf9b973e8 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 @@ -7,7 +7,7 @@ import { ChipInput, type ComboboxOptionGroup, cn, - DashedDividerLine, + FieldDisclosure, FieldDivider, Label, Loader, @@ -16,7 +16,7 @@ import { Tooltip, toast, } from '@sim/emcn' -import { ArrowLeft, ChevronDown, SquareArrowUpRight, X } from '@sim/emcn/icons' +import { ArrowLeft, SquareArrowUpRight, X } from '@sim/emcn/icons' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { findValidationIssue, isValidationError } from '@/lib/api/client/errors' @@ -838,23 +838,9 @@ export function WorkflowSidebarBody({ )} {selectedWorkflowId && ( <> -
- - - -
+ setShowAdvanced((v) => !v)}> + {showAdvanced ? 'Hide additional fields' : 'Show additional fields'} + {showAdvanced && ( <> - - - - + + {displayAdvancedOptions + ? 'Hide additional fields' + : 'Show additional fields'} + )} {hasAdditionalFields && !canEditBlock && displayAdvancedOptions && (
diff --git a/packages/emcn/src/components/field-divider/field-divider.dom.test.tsx b/packages/emcn/src/components/field-divider/field-divider.dom.test.tsx new file mode 100644 index 00000000000..ed3a624673d --- /dev/null +++ b/packages/emcn/src/components/field-divider/field-divider.dom.test.tsx @@ -0,0 +1,64 @@ +/** + * @vitest-environment jsdom + */ +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { FieldDisclosure } from './field-divider' + +let root: Root | null = null +let container: HTMLDivElement | null = null + +afterEach(() => { + act(() => root?.unmount()) + container?.remove() + root = null + container = null +}) + +describe('FieldDisclosure', () => { + it('leaves expansion to the caller and does not submit its surrounding form', () => { + const onClick = vi.fn() + const onSubmit = vi.fn((event) => event.preventDefault()) + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) + const render = (expanded: boolean, disabled = false) => ( +
+ + {expanded ? 'Hide additional fields' : 'Show additional fields'} + + +
+ ) + act(() => root?.render(render(false))) + const button = container.querySelector('button')! + expect(button.getAttribute('aria-expanded')).toBe('false') + expect(button.getAttribute('aria-controls')).toBe('additional-fields') + act(() => { + button.focus() + button.click() + }) + expect(document.activeElement).toBe(button) + expect(onClick).toHaveBeenCalledTimes(1) + expect(onSubmit).not.toHaveBeenCalled() + expect(button.getAttribute('aria-expanded')).toBe('false') + + act(() => root?.render(render(true))) + expect(button.getAttribute('aria-expanded')).toBe('true') + expect(button.textContent).toBe('Hide additional fields') + expect(container.querySelector('#additional-fields')?.hidden).toBe(false) + + act(() => root?.render(render(true, true))) + act(() => button.click()) + expect(onClick).toHaveBeenCalledTimes(1) + expect(onSubmit).not.toHaveBeenCalled() + }) +}) diff --git a/packages/emcn/src/components/field-divider/field-divider.tsx b/packages/emcn/src/components/field-divider/field-divider.tsx index 7408a21360f..731dac9995e 100644 --- a/packages/emcn/src/components/field-divider/field-divider.tsx +++ b/packages/emcn/src/components/field-divider/field-divider.tsx @@ -1,4 +1,6 @@ +import { ChevronDown } from '../../icons' import { cn } from '../../lib/cn' +import { Button, type ButtonProps } from '../button/button' const DASHED_DIVIDER_STYLE = { backgroundImage: @@ -54,4 +56,40 @@ function FieldDivider({ className, subblockMarker = false, ...props }: FieldDivi ) } -export { DashedDividerLine, FieldDivider } +export interface FieldDisclosureProps + extends Pick { + expanded: boolean +} + +/** + * Controlled disclosure between field groups. The caller owns the label, + * expanded state and fields; this supplies the divider lines and rotating chevron. + * + * @example + * + * {expanded ? 'Hide additional fields' : 'Show additional fields'} + * + */ +function FieldDisclosure({ expanded, children, ...props }: FieldDisclosureProps) { + return ( +
+ + + +
+ ) +} + +export { DashedDividerLine, FieldDisclosure, FieldDivider } diff --git a/packages/emcn/src/components/index.ts b/packages/emcn/src/components/index.ts index 6628b0a2d1b..29607eea0a6 100644 --- a/packages/emcn/src/components/index.ts +++ b/packages/emcn/src/components/index.ts @@ -153,7 +153,12 @@ export { dropdownMenuRowClass, } from './dropdown-menu/dropdown-menu' export { Expandable, ExpandableContent } from './expandable/expandable' -export { DashedDividerLine, FieldDivider } from './field-divider/field-divider' +export { + DashedDividerLine, + FieldDisclosure, + type FieldDisclosureProps, + FieldDivider, +} from './field-divider/field-divider' export { Info } from './info/info' export { InfoCard, From bdeefd3b9dbb375eebd9739315dd3394dcd9fddd Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Sat, 19 Sep 2026 14:35:37 -0700 Subject: [PATCH 2/4] fix(ui): show keyboard focus on field disclosures --- packages/emcn/src/components/field-divider/field-divider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/emcn/src/components/field-divider/field-divider.tsx b/packages/emcn/src/components/field-divider/field-divider.tsx index 731dac9995e..4b15d867f80 100644 --- a/packages/emcn/src/components/field-divider/field-divider.tsx +++ b/packages/emcn/src/components/field-divider/field-divider.tsx @@ -80,7 +80,7 @@ function FieldDisclosure({ expanded, children, ...props }: FieldDisclosureProps) variant='ghost' size={null} aria-expanded={expanded} - className='gap-1.5 whitespace-nowrap p-0 text-small' + className='gap-1.5 whitespace-nowrap p-0 text-small focus-visible:ring-2 focus-visible:ring-[color-mix(in_srgb,var(--text-muted)_30%,transparent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--surface-2)]' > {children} Date: Sat, 19 Sep 2026 17:55:10 -0700 Subject: [PATCH 3/4] improvement(ui): share table sidebar headers (#8048) Co-authored-by: Bill Leoutsakos --- .../column-config-sidebar.tsx | 19 ++++------ .../enrichments-sidebar/enrichment-config.tsx | 28 ++++++-------- .../enrichments-sidebar.tsx | 36 +++++++----------- .../table-sidebar-header.tsx | 37 +++++++++++++++++++ .../workflow-sidebar/workflow-sidebar.tsx | 28 ++++++-------- 5 files changed, 80 insertions(+), 68 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-header/table-sidebar-header.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx index abf3f9391a0..2ad4ebc773b 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx @@ -25,6 +25,10 @@ import { FieldError, RequiredLabel, } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/sidebar-fields' +import { + TableSidebarHeader, + TableSidebarHeaderAction, +} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-header/table-sidebar-header' import { useAddTableColumn, useUpdateColumn } from '@/hooks/queries/tables' import { SelectOptionsEditor } from '../select-field' import { columnTypeOptionsForTable } from './column-types' @@ -263,19 +267,12 @@ function ColumnConfigBody({ return (
-
+

Configure column

- -
+ +
{/* `disabled` on the fieldset reaches every native control inside, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx index f411281d21e..e158e4ab159 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx @@ -21,6 +21,10 @@ import type { ColumnDefinition, WorkflowGroup, WorkflowGroupOutput } from '@/lib import { columnMatchesRef, getColumnId } from '@/lib/table/column-keys' import { deriveOutputColumnName } from '@/lib/table/column-naming' import { FieldError } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/sidebar-fields' +import { + TableSidebarHeader, + TableSidebarHeaderAction, +} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-header/table-sidebar-header' import type { EnrichmentConfig as EnrichmentDef } from '@/enrichments/types' import { useAddWorkflowGroup, @@ -232,31 +236,21 @@ export function EnrichmentConfig({ return (
-
+
- +
- -
+ +
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichments-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichments-sidebar.tsx index 0aebd34cb4c..421055477fc 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichments-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichments-sidebar.tsx @@ -1,9 +1,13 @@ 'use client' import { useState } from 'react' -import { Button, ChipInput, cn } from '@sim/emcn' +import { ChipInput, cn } from '@sim/emcn' import { Search, X } from '@sim/emcn/icons' import type { ColumnDefinition, WorkflowGroup } from '@/lib/table' +import { + TableSidebarHeader, + TableSidebarHeaderAction, +} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-header/table-sidebar-header' import { ALL_ENRICHMENTS } from '@/enrichments' import { getEnrichment } from '@/enrichments/registry' import type { EnrichmentConfig as EnrichmentDef } from '@/enrichments/types' @@ -71,19 +75,12 @@ function EnrichmentsSidebarBody({ if (editGroup && !editEnrichment) { return (
-
+

Enrichment

- -
+ +

This enrichment ("{editGroup.enrichmentId}") is no longer available. Delete the column @@ -118,19 +115,12 @@ function EnrichmentsSidebarBody({ return (

-
+

Enrichments

- -
+ +
+ {children} +
+ ) +} + +interface TableSidebarHeaderActionProps extends ButtonHTMLAttributes { + 'aria-label': string +} + +export const TableSidebarHeaderAction = forwardRef< + HTMLButtonElement, + TableSidebarHeaderActionProps +>(({ className, ...props }, ref) => ( + + )}

- -
+ +
{/* Single-output mode renames this column directly. */} From 2d56098b9be09edeebde89b2b055bf8d7bffc97b Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos <157128530+BillLeoutsakosvl346@users.noreply.github.com> Date: Sun, 20 Sep 2026 20:31:46 -0700 Subject: [PATCH 4/4] improvement(ui): use shared sizing for small icon actions (#8090) * improvement(ui): use shared sizing for small icon actions * fix(ui): inherit the shared unlock icon color --------- Co-authored-by: Bill Leoutsakos --- .../chat/components/message/components/file-download.tsx | 2 +- .../app/(interfaces)/chat/components/message/message.tsx | 2 +- .../components/panel/components/editor/editor.tsx | 8 ++++---- .../components/panel/components/toolbar/toolbar.tsx | 7 +------ .../w/[workflowId]/components/panel/panel.tsx | 4 ++-- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx b/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx index 938ea3f2b3c..0e3c40e1f15 100644 --- a/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx @@ -254,7 +254,7 @@ export function ChatFileDownloadAll({ files }: ChatFileDownloadAllProps) { variant='ghost-secondary' onClick={handleDownloadAll} disabled={isDownloading} - className='p-0' + size='icon' > {isDownloading ? ( diff --git a/apps/sim/app/(interfaces)/chat/components/message/message.tsx b/apps/sim/app/(interfaces)/chat/components/message/message.tsx index 9458ed16be7..1b99ccf96cc 100644 --- a/apps/sim/app/(interfaces)/chat/components/message/message.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message/message.tsx @@ -274,7 +274,7 @@ export const ClientChatMessage = memo(function ClientChatMessage({ ) : (
@@ -505,7 +505,7 @@ export function Editor() { ) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 5f894cfc5f3..5337e108a8d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -915,7 +915,7 @@ export const Panel = memo(function Panel() {