UX Standards — 08C: Toolbar Button Popup/Behavior Reference
Governs: The exact popup, dialog, or behavior triggered by each UDT toolbar button.
Parent rules: See 08-UDT-TOOLBAR.md and 00-OVERVIEW-AND-CSS-RULES.md first.
Purpose
Every toolbar button either:
- Performs an immediate action (no UI shown)
- Opens a Dialog (blocking, user must confirm/cancel)
- Opens a floating panel (non-blocking, table stays interactive)
- Opens a Dropdown (immediate inline menu)
- Opens a Sheet (slide-over panel)
This document maps each button to its exact behavior.
Button Behavior Map
1. Reset & Refresh (RefreshCw)
| Property | Value |
|---|---|
| Type | Immediate action — no UI |
| Behavior | Resets: row selection, sorting, column filters, global filter (search), column visibility to default, column order to default. Then calls onRefresh() to refetch data. |
| Feedback | Table reloads with spinner on icon |
| Undo | None — this is destructive of filter/sort state |
// What "Reset & Refresh" resets:
table.resetRowSelection();
table.resetSorting();
table.resetColumnFilters();
table.setGlobalFilter('');
setSearchValue('');
onColumnVisibilityChange?.(defaultColumnVisibility);
onColumnOrderChange?.(defaultColumnOrder);
onClearFilters?.(); // clears advanced filter state
onRefresh?.(); // triggers data refetch
2. Column Selector (Columns3 icon)
| Property | Value |
|---|---|
| Type | ResizableDialog (draggable + resizable, non-blocking) |
| Trigger tooltip | "Manage columns" |
| Opens | "Manage Columns" dialog — draggable title bar, resizable edges |
| Default size | 400 × 450 px |
| Min size | 300 × 300 px |
| Max size | 600 × 700 px |
| Header | "Manage Columns" + "Drag to reorder. Click eye to toggle. (N of N visible)" |
| Bulk actions | Show All (Eye) / Hide All (EyeOff) / Reset (RotateCcw) — outline sm buttons |
| Column items | GripVertical drag handle + label + Eye/EyeOff toggle or "Required" badge |
| Required columns | enableHiding: false → show "Required" label, not eye icon |
| Drag-to-reorder | @dnd-kit/core with verticalListSortingStrategy |
| Auto-apply | Yes — changes apply immediately on toggle (autoApply=true) |
| Blocks table | No — table remains interactive |
| Doc | 07C-UDT-COLUMN-VISIBILITY-AND-DEFAULTS.md |
3. Row Density (Rows3)
| Property | Value |
|---|---|
| Type | DropdownMenu (inline menu) |
| Options | Compact / Default ✓ / Large |
| Behavior | Immediate — applies density to table rows on click |
| State | Checkmark on currently active density |
[Rows3 icon ▼]
┌──────────────────┐
│ Compact │
│ Default ✓ │
│ Large │
└──────────────────┘
4. Global Search (Search input field)
| Property | Value |
|---|---|
| Type | Inline input — no popup |
| Width | h-8 w-48 lg:w-64 |
| Icons | Search left (decorative), X right (clear, only when value present) |
| Behavior | Filters all visible columns in real-time as user types (global filter on TanStack Table) |
| Placeholder | "Search..." or custom searchPlaceholder prop |
| Clear | Clicking X clears the value and resets global filter |
5. Advanced Filters (Filter)
| Property | Value |
|---|---|
| Type | Floating panel (non-blocking — table stays interactive) |
| Opens | Advanced Filter dialog — draggable, resizable |
| Badge | Red badge showing count of active advanced filter conditions |
| Closes | Click Cancel or Apply Filters inside the panel |
| Doc | 09B-ADVANCED-FILTER-POPUP.md |
6. Clear Filters (FilterX)
| Property | Value |
|---|---|
| Type | Immediate action — no UI |
| Behavior | Clears both: (a) column-level filters, (b) advanced filter conditions. Keeps search/sort. |
| Disabled when | No filters active (shown with text-muted-foreground/40) |
| Tooltip when disabled | "Clear Filters (none active)" |
7. Clear Sort (ChevronsUpDown / ArrowDownUp)
| Property | Value |
|---|---|
| Type | Immediate action — no UI |
| Behavior | Calls table.resetSorting(). When onSortClick prop provided, calls that instead (for server-side sort reset). |
| Icon | ChevronsUpDown when no sort active; ArrowDownUp when sort active |
| Badge | Shows count of sorted columns |
| Disabled when | No sort active |
8. Import (Upload)
| Property | Value |
|---|---|
| Type | Dialog (blocking — 6-step modal wizard) |
| Opens | Import Wizard: ImportDialog component |
| Steps | Template → Upload → Map Fields → Options → Preview → Confirm |
| Size | max-w-4xl max-h-[90vh] |
| Doc | 10-IMPORT-WIZARD-MODAL.md |
9. Export (Download)
| Property | Value |
|---|---|
| Type | DropdownMenu then Dialog (two-step) |
| Step 1 | Format dropdown: CSV / TSV / Excel / JSON / XML / PDF |
| Step 2 | Export Data dialog: Records (All/Selected) + Columns (Visible/All) |
| Dialog size | sm:max-w-md |
| Doc | 08B-EXPORT-DIALOG.md |
[Download ▼]
┌─────────┐
│ CSV │ → opens Export Dialog
│ TSV │ → opens Export Dialog
│ Excel │ → opens Export Dialog
│ ─────── │
│ JSON │ → opens Export Dialog
│ XML │ → opens Export Dialog
│ ─────── │
│ PDF │ → opens Export Dialog or print preview
└─────────┘
10. View (Eye)
| Property | Value |
|---|---|
| Type | Sheet slide-over OR Dialog (page-specific) |
| Behavior | Opens a read-only detail view of the selected record(s) |
| Selection | Requires 1+ rows selected |
| Single selection | Opens that record's detail view |
| Multi selection | Opens first selected record with prev/next navigation |
| Common implementation | Sheet from right (sm:max-w-xl) with all record fields displayed |
// Typical View Sheet:
<Sheet open={viewOpen} onOpenChange={setViewOpen}>
<SheetContent side="right" className="sm:max-w-xl flex flex-col">
<SheetHeader>
<SheetTitle>{record.name}</SheetTitle>
<SheetDescription>View record details</SheetDescription>
</SheetHeader>
<div className="flex-1 overflow-y-auto py-4">
<RecordDetailView record={record} />
</div>
<div className="border-t pt-4 flex justify-between">
<Button variant="outline" onClick={() => setViewOpen(false)}>Close</Button>
{canEdit && <Button onClick={handleEdit}>Edit</Button>}
</div>
</SheetContent>
</Sheet>
11. Add (Plus)
| Property | Value |
|---|---|
| Type | Dialog (blocking) OR navigation to new record page |
| Behavior | Opens a create form for a new record of this doctype |
| No selection required | Button always enabled (when canCreate permission) |
| Common implementation | Dialog sm:max-w-2xl with form, or router.push('/new') |
// Add dialog pattern:
<Dialog open={addOpen} onOpenChange={setAddOpen}>
<DialogContent className="sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Add {doctype}</DialogTitle>
</DialogHeader>
<CreateForm
onSuccess={(newRecord) => {
setAddOpen(false);
refetch();
toast.success(`${doctype} created`);
}}
/>
</DialogContent>
</Dialog>
12. Copy (Copy)
| Property | Value |
|---|---|
| Type | Immediate action — no UI |
| Behavior | Copies the selected row's data as formatted text to clipboard |
| Selection | Requires exactly 1 row |
| Disabled when | 0 rows or 2+ rows selected |
| Feedback | toast.success('Copied to clipboard') |
| Format | Tab-delimited: Field1\tValue1\tField2\tValue2... |
Note: This copies the entire row to clipboard. For cell-level copying, use the right-click context menu (see 07B-UDT-CELL-SELECTION-AND-COPY.md).
13. Duplicate (Files)
| Property | Value |
|---|---|
| Type | Dialog (confirmation) OR immediate |
| Behavior | Creates a copy of the selected record(s) with all field values copied |
| Selection | Requires 1+ rows |
| Confirmation | "Duplicate N record(s)? This will create N new records." |
| After action | Table refreshes, new record(s) shown (may scroll to top) |
// Confirmation dialog for bulk duplicate:
<AlertDialog>
<AlertDialogContent>
<AlertDialogTitle>Duplicate {selectedCount} record(s)?</AlertDialogTitle>
<AlertDialogDescription>
This will create {selectedCount} new record(s) with all field values copied.
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleDuplicate}>Duplicate</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
14. Bulk Edit (SquarePen)
| Property | Value |
|---|---|
| Type | Dialog (sm:max-w-md, blocking) |
| Title | "Bulk Edit" |
| Description | "Edit N selected rows. Select fields to modify." |
| Tooltip | "Bulk edit N records" (dynamic count in tooltip) |
| Behavior | Shows ALL table fields as checkboxes. User checks a field to reveal its input. Only checked fields are updated. |
| Selection | Requires 2+ rows (selectedRows.length > 1) |
| Field types | text (Input), number (Input type=number), select (Select dropdown), boolean (Yes/No Select) |
| Apply disabled | When no fields are checked OR while isLoading |
| Feedback | toast.success('Updated N records') |
| Component | BulkEditDialog from @/components/bulk-edit-dialog |
UI Pattern — Checkbox-activated fields: Each field shows as a checkbox row. The input is HIDDEN until the field is checked:
┌──────────────────────────────────────────────┐
│ Bulk Edit [✕] │
│ Edit 2 selected rows. Select fields to │
│ modify. │
├──────────────────────────────────────────────┤
│ ☐ select │ ← unchecked = just label shown
│ ☐ username │
│ ☐ fullName │
│ ☐ email │
│ ☐ phone_number │
│ ☐ status │ ← when checked: input appears below
│ ☑ status │
│ [Active ▼] │ ← Select input revealed when checked
│ ☐ tenant_name │
│ ↕ (scrollable) │
├──────────────────────────────────────────────┤
│ [Cancel] [Apply to 2 rows] │
└──────────────────────────────────────────────┘
"Apply to N rows" button is grayed/disabled until at least one field checkbox is checked.
15. Edit (Pencil)
| Property | Value |
|---|---|
| Type | Dialog (blocking) OR navigation to edit page |
| Behavior | Opens the full edit form for the selected record |
| Selection | Requires 1+ rows; if multiple, opens first with prev/next navigation |
| Common implementation | Dialog sm:max-w-2xl with pre-filled form |
16. Delete (Trash2)
| Property | Value |
|---|---|
| Type | AlertDialog (blocking confirmation) |
| Behavior | Permanently deletes selected record(s) after confirmation |
| Selection | Requires 1+ rows |
| Confirmation text | "Delete N record(s)? This action cannot be undone." |
| Primary CTA | variant="destructive" — "Delete N Record(s)" |
| After action | Table refreshes, deleted records removed |
<AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Delete {selectedCount} record{selectedCount !== 1 ? 's' : ''}?
</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. {selectedCount > 1
? `These ${selectedCount} records will be permanently deleted.`
: 'This record will be permanently deleted.'}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={handleDelete}
>
Delete {selectedCount} Record{selectedCount !== 1 ? 's' : ''}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
17. Upload Attachment (Paperclip)
| Property | Value |
|---|---|
| Type | Dialog (blocking) |
| Behavior | Opens file picker to upload file(s) attached to the selected record |
| Selection | Requires 1+ rows |
| Enabled | Only when showAttachmentActions={true} on toolbar |
| Common implementation | Dialog with drag-and-drop zone + file list |
18. Download Attachment (FileDown)
| Property | Value |
|---|---|
| Type | Immediate action OR Dialog if multiple attachments |
| Behavior | Downloads the attachment(s) from the selected record |
| Selection | Requires exactly 1 row |
| Single attachment | Downloads immediately (no UI) |
| Multiple attachments | Opens a small Dialog listing available files to download |
19. Send Email (Mail)
| Property | Value |
|---|---|
| Type | Dialog (blocking) |
| Behavior | Opens email compose dialog pre-addressed to selected user(s) |
| Selection | Requires 1+ rows |
| Enabled | Only when showEmailAction={true} on toolbar |
20. Help (HelpCircle)
| Property | Value |
|---|---|
| Type | Sheet (right slide-over) OR persistent popup window |
| Behavior | Opens contextual help content for the current page/doctype |
| Common implementation | Sheet with FAQ content, OR openPersistentPopup('help', '/help/product-setup', 'help-window') |
Summary Quick Reference
| Button | Popup Type | Blocks Table? | Selection Required |
|---|---|---|---|
| Reset & Refresh | None (immediate) | No | No |
| Column Selector | Popover | No | No |
| Row Density | DropdownMenu | No | No |
| Search | Inline input | No | No |
| Advanced Filters | Floating panel | No | No |
| Clear Filters | None (immediate) | No | No |
| Clear Sort | None (immediate) | No | No |
| Import | Dialog (wizard, 6 steps) | Yes | No |
| Export | DropdownMenu → Dialog | Yes | No |
| View | Sheet (right) | Yes | 1+ rows |
| Add | Dialog | Yes | No |
| Copy | None (immediate) | No | Exactly 1 |
| Duplicate | AlertDialog | Yes | 1+ rows |
| Bulk Edit | Dialog | Yes | 2+ rows |
| Edit | Dialog | Yes | 1+ rows |
| Delete | AlertDialog (destructive) | Yes | 1+ rows |
| Upload Attachment | Dialog | Yes | 1+ rows |
| Download Attachment | None or Dialog | Varies | Exactly 1 |
| Send Email | Dialog | Yes | 1+ rows |
| Help | Sheet or popup | No | No |