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: boolean
parentId?: string
(optional; when provided, modal will create a connecting edge)initialValues?
(unused for Add)onSubmit(values: NodeFormValues)
whereNodeFormValues
is{ 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
Hierarchy
from its dropdown (defaults to the active hierarchy inHierarchyContext
). This updates the active hierarchy context. - The
Level
dropdown is populated with levels corresponding to the selectedHierarchy
. - User selects a
Level
(or the system suggests a default based onparentId
or as the first level). - On submit, the
NodeFormModal
passes the chosenlabel
,type
,hierarchyId
, andlevelId
to itsonSubmit
callback. - This data is typically used by
useGraphState.addNode
to construct anaddNode
GraphQL mutation with an explicithierarchyAssignments
field containing the selectedhierarchy.id
andlevel.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.form
configurations - Buttons styled using
theme.components.button
definitions
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
onSave
with 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.tab
configurations - Form elements follow
theme.components.form
patterns
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
NodeFormModal
and 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
SettingsModal
andNodeFormModal
- 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
hierarchies
fromHierarchyContext
. - Changing selection calls
setHierarchyId
(fromHierarchyContext
), updating the application's active hierarchy. - The
GraphView
(viauseGraphState
) listens to changes inhierarchyId
from the context and reloads/re-filters graph data accordingly. - Important: For the
X-Hierarchy-Id
header (used byApiService
for mutations) to reflect this change,localStorage.getItem('hierarchyId')
must be updated when the selection changes. The example above includes thislocalStorage.setItem
call. Ideally, thislocalStorage
update is managed centrally alongside the context state update.