UI Elements Specification
This document describes the primary UI components in the MakeItMakeSense.io graph interface: context menus, the Add-Node modal, and the Edit-Node drawer. It serves as both developer reference and human-friendly guide.
1. Context Menus
Implemented by <ContextMenu> via ContextMenuContext. Right-click on the graph triggers one of three menus:
1.1 Background Context Menu
Trigger
• Right-click on empty canvas
Items
| Label | Icon | Shortcut | Handler | Description |
|---|---|---|---|---|
| Add Node | ➕ | A | openAddModal(parentId) | Opens the Add-Node modal for creating a new root or child |
| Load Complete Graph | 📂 | L | loadCompleteGraph() | Fetches & displays entire graph using iterative traversal |
| Clear Graph | 🗑️ | Ctrl+Del | resetGraph() | Clears canvas (prompts for confirmation) |
1.2 Node Context Menu
Trigger
• Right-click on a node
Items
| Label | Icon | Shortcut | Handler | Description |
|---|---|---|---|---|
| Add Connected Node | ➕ | A | openAddModal(nodeId) | Opens Add-Node modal with this node as parent |
| Edit Node | ✏️ | Ctrl+E | openEditDrawer(node) | Opens right-side drawer to edit properties |
| Delete Node | 🗑️ | Del | onDeleteNode(nodeId) | Permanently remove node & its edges |
| Hide Node | 👁️🗨️ | H | onHideNode(nodeId) | Temporarily hide node & edges |
| Expand Children | ▶️ | E | onNodeExpand(nodeId) | Load & show direct children (immediate neighbors) |
| Expand Descendents | ▶️▶️ | E, then E | onExpandDesc(nodeId) | (Future Feature) Recursively show all descendants |
| Collapse Descendents | ◀️◀️ | C | onCollapseDesc(nodeId) | (Future Feature) Collapse this node's descendants |
1.3 Multi-Node Operations
Trigger
• Right-click with multiple nodes selected
Items mirror Node Context but operate on nodeIds array:
| Label | Icon | Shortcut | Handler | Description |
|---|---|---|---|---|
| Add Connected Nodes | ➕ | A | openAddModal(nodeId) | Open modal per selected node |
| Edit Nodes | ✏️ | Ctrl+E | openEditDrawer(node) | Open drawer per selected node |
| Delete Nodes | 🗑️ | Del | onDeleteNodes(nodeIds) | Remove all selected nodes & edges |
| Hide Nodes | 👁️🗨️ | H | onHideNodes(nodeIds) | Hide all selected nodes & edges |
| Expand Children (All) | ▶️ | E | onNodeExpandBatch(nodeIds) | Load children for all selected nodes |
| Expand Descendents (All) | ▶️▶️ | E, then E | onExpandDescBatch(nodeIds) | (Future Feature) Recursively expand for all selected |
| Collapse Descendents (All) | ◀️◀️ | C | onCollapseDescBatch(nodeIds) | (Future Feature) Collapse descendants for all selected |
2. Add-Node Modal
Component: <NodeFormModal> (in /frontend/src/components/NodeFormModal.tsx); controlled by UIContext.
Trigger
• openAddModal(parentId?) from context menu
Props
open: booleanparentId?: string(optional; when provided, modal will create a connecting edge)initialValues?(unused for Add)onSubmit(values: NodeFormValues)whereNodeFormValuesis{ label: string; type: string; hierarchyId: string; levelId: string }onCancel()
Fields
- Label (text input, required)
- Type (dropdown: concept, example, question)
- Hierarchy (dropdown: list of available hierarchies from
HierarchyContext) - Level (dropdown: list of levels for the selected hierarchy, from
HierarchyContext)
Actions
- Save → validate & call
onSubmit(which then typically callsuseGraphState.addNode), close modal - Cancel → call
onCancel, close modal
Data Flow
- User selects a
Hierarchyfrom its dropdown (defaults to the active hierarchy inHierarchyContext). This updates the active hierarchy context. - The
Leveldropdown is populated with levels corresponding to the selectedHierarchy. - User selects a
Level(or the system suggests a default based onparentIdor as the first level). - On submit, the
NodeFormModalpasses the chosenlabel,type,hierarchyId, andlevelIdto itsonSubmitcallback. - This data is typically used by
useGraphState.addNodeto construct anaddNodeGraphQL mutation with an explicithierarchyAssignmentsfield containing the selectedhierarchy.idandlevel.id. The backend API then uses this explicit assignment.
Styling
- Uses centralized theme system for consistent styling
- Modal overlay and content styled via
theme.components.modal - Form fields use
theme.components.formconfigurations - Buttons styled using
theme.components.buttondefinitions
For complete theme system documentation, see Frontend Development - Theme System
Accessibility/UI
- Centered overlay with semi-opaque backdrop
- Focus trapping, Esc closes
3. Edit-Node Drawer
Component: <NodeDrawer> (in /frontend/src/components/NodeDrawer.tsx); controlled by UIContext.
Trigger
• openEditDrawer(nodeData) (Typically triggered by double-clicking a node or from context menu)
Layout
- Fixed panel on right (320px wide)
- Tabs: Info, Links (Future), History (Future)
Info Tab
- Displays node properties (label, type). Editing hierarchy assignment via this drawer is not currently specified.
- Save → call
onSavewith updated values, close drawer - Cancel → call
onClose, close drawer
Styling
- Uses centralized theme system for consistent drawer styling
- Drawer background and border styled via
theme.components.drawer - Tab styles use
theme.components.drawer.tabconfigurations - Form elements follow
theme.components.formpatterns
For complete theme system documentation, see Frontend Development - Theme System
Future Tabs
- Links and History placeholders for future development.
4. UI Context
File: /frontend/src/context/UIContext.tsx
Manages state for modals and drawers:
openAddModal(parentId?)/closeAddModal()openEditDrawer(nodeData)/closeEditDrawer()
Context consumers: ContextMenuContext, App.tsx, and components that trigger these UI elements.
5. Theme System Reference
UI components use the centralized theme system for consistent styling across the application.
For complete theme system documentation, including:
- Architecture overview and file structure
- Design tokens and theme configuration
- Dynamic level colors and style utilities
- Usage guidelines and best practices
See: Frontend Development - Theme System & Styling Architecture
6. Testing Strategy
- Unit Tests:
<ContextMenu>renders correct items for each menu type.<NodeFormModal>and<NodeDrawer>form flows, state changes, and callbacks.- Theme utilities return correct style objects.
- End-to-End:
- Verify context-menu → modal/drawer open/cancel/save for single & multi-node scenarios.
- Test hierarchy and level selection in
NodeFormModaland its effect on node creation. - Verify consistent styling across all components.
Keep this doc in sync: update here first, then adjust code and tests as needed.
7. Admin Modal
Component: <AdminModal> (in /frontend/src/components/AdminModal.tsx); controlled by UIContext.
Architecture
- Uses shared modal components:
<ModalOverlay>,<ModalContainer>,<ModalHeader>,<TabNavigation>,<ModalContent> - Consistent with other modals: Follows the same architectural patterns as
SettingsModalandNodeFormModal - Theme-integrated styling: Uses centralized theme system via
theme.components.adminModal.*
Authentication
- Admin key authentication required: Users must provide a valid admin API key to access admin tools
- Login form: Displays when not authenticated, with password visibility toggle
- Logout functionality: Proper state management and cleanup on logout
Tabs
Tests Tab
- Test execution: Unit, Integration, Integration-Real, and Linting tests
- StatusBadge integration: Consistent status indicators for test results (
running,completed,failed) - Real-time updates: Polling for test completion with live status updates
- Results display: Detailed test summaries with pass/fail counts and error details
Tenants Tab
- Multi-tenant management: Create, delete, reset tenants (Enterprise features)
- Tenant operations: Clear data, clear schema, push schema, seed data
- StatusBadge integration: Health status indicators (
healthy,not-accessible,error,unknown) - Schema viewing: Modal for viewing tenant schema content with copy functionality
Styling
- Shared modal architecture: Uses
width={600}andheight="70vh"like other modals - Theme-based buttons: All action buttons use
buildTenantActionButtonStyle()andbuildTestButtonStyle() - Consistent scrollbars: Uses
buildScrollbarStyle()for content areas - StatusBadge components: Replaces inline status spans for consistency
Testing
- Comprehensive unit tests: Located in
frontend/tests/unit/components/AdminModal.test.tsx - Tests cover: Modal visibility, authentication flow, tab navigation, shared architecture, theme integration
- Follows established patterns: Similar test structure to
NodeFormModal.test.tsx
Usage Example
import { useUIContext } from '../hooks/useUI';
function SomeComponent() {
const { openAdminModal } = useUIContext();
return (
<button onClick={openAdminModal}>
Open Admin Tools
</button>
);
}
8. Hierarchy Selector
The hierarchy selector is a dropdown, typically in the application header, allowing users to switch the active hierarchy for graph viewing and operations.
Location Example: frontend/src/App.tsx
Markup Example:
import { useHierarchyContext } from './context/HierarchyContext';
function AppHeader() {
const { hierarchies, hierarchyId, setHierarchyId } = useHierarchyContext();
const handleHierarchyChange = (newHierarchyId: string) => {
setHierarchyId(newHierarchyId);
// For ApiService to pick up the change for X-Hierarchy-Id header,
// localStorage should also be updated here or within setHierarchyId in the context.
localStorage.setItem('hierarchyId', newHierarchyId);
};
return (
<div className="app-header">
<label htmlFor="hierarchy-select">Hierarchy:</label>
<select
id="hierarchy-select"
value={hierarchyId}
onChange={e => handleHierarchyChange(e.target.value)}
aria-label="Select hierarchy"
>
{hierarchies.map(h => (
<option key={h.id} value={h.id}>
{h.name}
</option>
))}
</select>
</div>
);
}
Behavior:
- On mount, the dropdown lists all fetched
hierarchiesfromHierarchyContext. - Changing selection calls
setHierarchyId(fromHierarchyContext), updating the application's active hierarchy. - The
GraphView(viauseGraphState) listens to changes inhierarchyIdfrom the context and reloads/re-filters graph data accordingly. - Important: For the
X-Hierarchy-Idheader (used byApiServicefor mutations) to reflect this change,localStorage.getItem('hierarchyId')must be updated when the selection changes. The example above includes thislocalStorage.setItemcall. Ideally, thislocalStorageupdate is managed centrally alongside the context state update.