npm package: @iris711/form-renderer
Description: Universal form rendering component for CUBE Apply and CUBE Enforce
Author: CUBE Team
Latest version: 1.1.0

# @cube/form-renderer

Universal form rendering component for CUBE Apply and CUBE Enforce applications. This package enables both platforms to render forms identically with full feature parity while maintaining platform-specific customisations.

## Features

- **20+ Field Types**: text, number, textarea, email, phone, date, datetime, radio, select, checkbox, multi-select, file, photo_upload, and more
- **Advanced CUBE Fields**: location (Address Search), business_lookup (Companies House), activities_commodities, party_creation, choice_with_pricing, price_calculation
- **Layout Fields**: h1, h2, h3, paragraph, divider, spacer
- **Component Injection**: Fully customisable UI components, icons, and styling
- **Mobile Optimised**: Pre-built mobile components with touch-friendly interfaces
- **Value Handling**: Proper nullish coalescing to preserve falsy values (0, false, empty arrays)
- **Conditional Visibility**: Show/hide fields based on form state
- **Input Masking**: Support for formatted inputs (phone numbers, etc.)

## Installation

```bash
npm install @cube/form-renderer
```

## Peer Dependencies

This package requires:

```json
{
  "react": "^18.0.0",
  "react-dom": "^18.0.0"
}
```

## Basic Usage

### CUBE Apply Integration (with shadcn/ui)

```tsx
import { SmartFormFieldRenderer } from '@cube/form-renderer';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipProvider, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip';
import { HelpCircle, Search, X, AlertCircle } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';

// Import specialized CUBE components
import { ActivitiesCommoditiesField } from '@/components/ActivitiesCommoditiesField';
import { PartyCreationField } from '@/components/PartyCreationField';
import AddressEntrySelector from '@/components/AddressEntrySelector';
import { ChoiceWithPricingField } from '@/components/form-fields/ChoiceWithPricingField';
import { PriceCalculationField } from '@/components/form-fields/PriceCalculationField';

// Import utilities
import { shouldShowField, applyInputMask, getRawValue } from '@/lib/formEnhancements';
import { API } from '@/lib/constants';

function MyFormRenderer({ field, value, onChange, formValues }) {
  return (
    <SmartFormFieldRenderer
      field={field}
      value={value}
      onChange={onChange}
      formValues={formValues}
      components={{
        Input,
        Label,
        Button,
        Tooltip,
        TooltipProvider,
        TooltipTrigger,
        TooltipContent,
      }}
      icons={{
        HelpIcon: HelpCircle,
        SearchIcon: Search,
        CloseIcon: X,
        AlertIcon: AlertCircle,
      }}
      specializedComponents={{
        ActivitiesCommoditiesField,
        PartyCreationField,
        AddressEntrySelector,
        ChoiceWithPricingField,
        PriceCalculationField,
      }}
      utilityFunctions={{
        shouldShowField,
        applyInputMask,
        getRawValue,
      }}
      useQuery={useQuery}
      apiConstants={{
        BUSINESSES_BASE: API.BUSINESSES.BASE,
        BUSINESSES_SEARCH: API.BUSINESSES.SEARCH,
      }}
    />
  );
}
```

### CUBE Enforce Integration (Mobile Optimised)

```tsx
import { SmartFormFieldRenderer, MobileInput, MobileLabel, MobileButton } from '@cube/form-renderer';
import { HelpCircle, Search, X, AlertCircle } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';

// Import specialized CUBE components (these should exist in CUBE Enforce)
import { ActivitiesCommoditiesField } from '@/components/ActivitiesCommoditiesField';
import { PartyCreationField } from '@/components/PartyCreationField';
import AddressEntrySelector from '@/components/AddressEntrySelector';

function MobileFormRenderer({ field, value, onChange, formValues }) {
  return (
    <SmartFormFieldRenderer
      field={field}
      value={value}
      onChange={onChange}
      formValues={formValues}
      components={{
        Input: MobileInput,
        Label: MobileLabel,
        Button: MobileButton,
        // Mobile doesn't use complex tooltips
        Tooltip: ({ children }) => <>{children}</>,
        TooltipProvider: ({ children }) => <>{children}</>,
        TooltipTrigger: () => null,
        TooltipContent: () => null,
      }}
      icons={{
        HelpIcon: HelpCircle,
        SearchIcon: Search,
        CloseIcon: X,
        AlertIcon: AlertCircle,
      }}
      specializedComponents={{
        ActivitiesCommoditiesField,
        PartyCreationField,
        AddressEntrySelector,
        // Pricing fields may not be needed in enforcement
      }}
      useQuery={useQuery}
      classNames={{
        container: "mb-4",
        label: "block text-base font-semibold text-gray-900 mb-2",
        input: "w-full px-4 py-3 border-2 border-gray-300 rounded-lg text-base",
      }}
    />
  );
}
```

## API Reference

### SmartFormFieldRendererProps

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `field` | `FormField` | Yes | The field definition object |
| `value` | `string \| string[] \| boolean` | Yes | Current field value |
| `onChange` | `(value: any) => void` | Yes | Value change handler |
| `formValues` | `Record<string, any>` | Yes | All form values for conditional logic |
| `allFields` | `FormField[]` | No | All form fields (needed for price_calculation) |
| `components` | `UIComponents` | Yes | UI component injection |
| `icons` | `IconComponents` | Yes | Icon component injection |
| `specializedComponents` | `SpecializedComponents` | No | Advanced field components |
| `utilityFunctions` | `UtilityFunctions` | No | Utility function injection |
| `useQuery` | `QueryHook` | No | TanStack Query hook (required for business_lookup) |
| `classNames` | `StyleClassNames` | No | Custom styling classes |
| `apiConstants` | `object` | No | API endpoint configuration |

### FormField Type

```typescript
interface FormField {
  id: string;
  label: string;
  type: string;
  required?: boolean;
  placeholder?: string;
  options?: string[];
  configuration?: {
    tooltip?: string;
    helpText?: string;
    inputMask?: string;
    conditionalVisibility?: {
      enabled: boolean;
      conditions: Array<{
        fieldValue: string;
        operator: string;
        value: any;
      }>;
      logic: 'AND' | 'OR';
    };
    minDate?: string;
    maxDate?: string;
    spacingSize?: 'small' | 'medium' | 'large' | 'custom';
    customSpacing?: number;
    // ... other field-specific configuration
  };
}
```

## Supported Field Types

### Basic Fields
- `text` - Single-line text input
- `number` - Numeric input
- `textarea` - Multi-line text input
- `email` - Email address input
- `phone` - Phone number input with optional masking
- `date` - Date picker with relative date support
- `datetime` - Date and time picker
- `radio` - Radio button group
- `select` - Dropdown select
- `checkbox` - Single checkbox or checkbox group
- `multi-select` - Multiple selection checkboxes
- `file` - File upload
- `photo_upload` - Image upload with AI analysis

### Layout Fields
- `h1`, `h2`, `h3` - Headings
- `paragraph` - Paragraph text
- `divider` - Horizontal line separator
- `spacer` - Vertical spacing

### Advanced Fields (require specialized components)
- `location` - Address search with gazetteer integration
- `business_lookup` - Companies House business search
- `activities_commodities` - UK licence activities/commodities selector
- `party_creation` - Configurable party forms
- `choice_with_pricing` - Radio buttons with dynamic pricing
- `price_calculation` - Formula-based price calculation

## Value Handling

The package uses **nullish coalescing (`??`)** throughout to preserve falsy values:

```tsx
// ✅ Correct - preserves 0, false, empty arrays
value={value ?? ''}

// ❌ Wrong - 0 becomes '', false becomes '', [] becomes ''
value={value || ''}
```

This ensures:
- Numeric `0` is preserved in number fields and price calculations
- Boolean `false` is preserved in checkbox fields
- Empty arrays `[]` are preserved in multi-select fields

## Mobile Components

Pre-built mobile-optimised components are included:

```tsx
import { 
  MobileInput,
  MobileLabel,
  MobileButton,
  MobileTooltip,
  MobileTooltipProvider,
  MobileTooltipTrigger,
  MobileTooltipContent 
} from '@cube/form-renderer';
```

Features:
- **16px font size** on inputs (prevents iOS zoom on focus)
- **Touch-friendly targets** (larger padding, bigger hit areas)
- **Simplified tooltips** (not suitable for touch devices)
- **Active state animations** for better feedback

## Utility Functions

Export utility functions for custom implementations:

```tsx
import { 
  shouldShowField,
  applyInputMask,
  getRawValue,
  parseRelativeDate,
  formatDateForInput 
} from '@cube/form-renderer';

// Check conditional visibility
if (shouldShowField(field.configuration?.conditionalVisibility, formValues)) {
  // Render field
}

// Apply input mask
const masked = applyInputMask('1234567890', '(999) 999-9999');
// Result: (123) 456-7890

// Parse relative dates
const date = parseRelativeDate('today'); // Date object for today
const future = parseRelativeDate('+7 days'); // 7 days from now
```

## TypeScript Support

Full TypeScript definitions are included. Import types as needed:

```tsx
import type { 
  FormField,
  SmartFormFieldRendererProps,
  UIComponents,
  IconComponents,
  SpecializedComponents 
} from '@cube/form-renderer';
```

## License

MIT

## Support

For issues or questions, contact the CUBE development team.