PSA/docs/ui/ui_automation_ids.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

5.8 KiB

UI Automation IDs

Overview

The UI reflection system provides a live, high-level JSON description of the application's UI state. To ensure proper integration with automated testing and LLM-driven interactions, all UI components must maintain consistency between their UI state IDs and DOM data-automation-id attributes.

Parent-Child Relationships

Components automatically inherit their parent's ID through React context, eliminating the need to manually wire parentId everywhere:

// Parent container sets the context
<ReflectionContainer id="ticketing-dashboard">
  {/* Children automatically know their parent */}
  <TicketFilters /> {/* gets ticketing-dashboard as parent */}
  <TicketTable />  {/* gets ticketing-dashboard as parent */}
</ReflectionContainer>

Naming Conventions

Follow these patterns for component IDs:

  1. Screen/Page Components:

    // Base page ID
    id="ticketing-dashboard"
    
  2. Subcontainers:

    // Major sections use parent ID + purpose
    id={`${parentId}-filters`}    // ticketing-dashboard-filters
    id={`${parentId}-table`}      // ticketing-dashboard-table
    
  3. UI Components:

    // Individual elements use parent ID + type
    id={`${parentId}-status-select`}  // ticketing-dashboard-filters-status-select
    id={`${parentId}-search-input`}   // ticketing-dashboard-filters-search-input
    

Sidebar Menu ID Convention

The sidebar menu items use a double-prefix pattern in their DOM automation IDs. The Sidebar component generates an id of menu-{name} for each item, which is then passed to SidebarMenuItem. The SidebarMenuItem renders the element with a data-automation-id of sidebar-menu-menu-{name}, resulting in the menu- prefix appearing twice.

For example, the Home menu item renders as:

data-automation-id="sidebar-menu-menu-home"

Other examples:

  • Tickets: sidebar-menu-menu-tickets
  • Companies: sidebar-menu-menu-companies
  • Settings: sidebar-menu-menu-settings

When writing tests or automation scripts targeting sidebar items, use the full double-prefix form (sidebar-menu-menu-{name}).

The useAutomationIdAndRegister Hook

Use this hook to combine UI reflection registration and data-automation-id props:

function MyComponent({ id, ...props }) {
  // Register with reflection system and get DOM props
  const { automationIdProps, updateMetadata } = useAutomationIdAndRegister<ContainerComponent>({
    id,                    // Optional - will auto-generate if not provided
    type: 'container',
    label: 'My Component'
  });

  return <div {...automationIdProps} {...props} />;
}

Features

  1. Automatic ID Generation:

    // With explicit ID
    const { automationIdProps } = useAutomationIdAndRegister({
      id: `${parentId}-filters`,
      type: 'container'
    });
    
    // With auto-generated ID
    const { automationIdProps } = useAutomationIdAndRegister({
      type: 'container'  // generates like: container-1, container-2, etc.
    });
    
  2. Parent Context Integration:

    // Auto-generated IDs include parent context
    parentId: 'my-screen'
    auto-generated: 'my-screen-button-1'
    
  3. Single Source of Truth:

    • One hook call provides both reflection registration and DOM attributes
    • No chance of mismatch between UI state and DOM
    • Type-safe through TypeScript generics

Core Components

Over 52 UI components now implement the useAutomationIdAndRegister pattern, including but not limited to:

  • Button (packages/ui/src/components/Button.tsx)
  • Input (packages/ui/src/components/Input.tsx)
  • Dialog (packages/ui/src/components/Dialog.tsx)
  • SearchableSelect, CustomSelect, AsyncSearchableSelect
  • TextArea, SearchInput, TagInput
  • ClientPicker, UserPicker, ContactPicker, CountryPicker
  • DateTimePicker, DatePicker, TimePicker
  • GenericDialog, CategoryPicker, BoardPicker

When creating new components that use the UI reflection system, follow these examples to maintain consistency.

Testing

When writing tests, always use the component's ID to locate elements:

// Good - uses data-automation-id that matches UI state
cy.get('[data-automation-id="ticketing-dashboard-filters-status-select"]').click();

// Bad - uses other selectors that might change
cy.get('.status-select').click();
cy.get('select:contains("Status")').click();

Benefits

  1. Automated Testing:

    • Consistent, predictable IDs make tests reliable
    • Parent-child relationships reflected in ID structure
    • Auto-generated IDs maintain uniqueness
  2. LLM Integration:

    • Hierarchical component structure is clear from IDs
    • Consistent naming makes UI state easy to understand
    • Automatic parent-child tracking improves accuracy
  3. Developer Experience:

    • No manual parentId prop passing
    • Auto-generated IDs reduce boilerplate
    • Single hook call for all UI reflection needs
    • Type-safe through TypeScript
  4. Maintenance:

    • Consistent naming conventions
    • Clear component hierarchy
    • Reduced coupling between tests and implementation
    • Single source of truth for IDs

Implementation Details

The UI reflection system uses React's Context API to maintain a live representation of the UI state. Each component that registers with the system contributes to this state, and the data-automation-id attributes provide a direct mapping between the logical UI state and the actual DOM elements.

The system now includes:

  • ReflectionParentContext for automatic parent-child relationships
  • useAutomationIdAndRegister hook for unified registration and DOM props
  • Automatic ID generation with consistent naming patterns
  • Type-safe component registration through TypeScript

See also: