Skip to content

Development

Christopher Schwarz edited this page Aug 4, 2025 · 6 revisions

Development Guide

πŸ› οΈ Development Setup

Prerequisites

  • Node.js 16+
  • Directus v11.0.0+
  • TypeScript knowledge
  • pnpm (preferred) or npm

Getting Started

# Clone and install
git clone https://github.com/smartlabsAT/directus-expandable-blocks.git
cd directus-expandable-blocks
pnpm install  # Preferred for better performance
# or
npm install

# Start development
npm run dev

Development Commands

npm run build        # Build the extension (required for API testing)
npm run dev          # Development mode with watcher
npm run test -- --run # Run unit tests (use --run to avoid watcher)
npm run test:ui      # Vitest UI interface
npm run test:coverage -- --run # With coverage reports
npm run type-check   # TypeScript checks
npm run lint         # Run ESLint
npm run lint:fix     # Auto-fix linting issues
npm run qodana       # Run Qodana code quality analysis

# Docker
docker compose restart directus # Restart after build

Note:

  • For interface development, an automatic watcher rebuilds changes
  • For API testing, you MUST run npm run build manually
  • E2E tests have been removed from the project
  • Tests achieve 100% pass rate (533 tests passing, 5 skipped)
  • Package management: Use pnpm for installation, npm for scripts

πŸ› Debugging

Enable Debug Mode

{
  "options": {
    "debugMode": true
  }
}

This logs:

  • State changes
  • Dirty detection
  • Save/discard events
  • Watcher triggers

Common Issues

Issue Solution
Blocks not loading Check if props.value exists in watcher
Sort not saving Verify sort_field in relation settings
Always dirty Use deepClone() for original states
Props timing Process data in watchers, not onMounted

πŸ“ Project Structure

src/
β”œβ”€β”€ interface.vue          # Main orchestrator
β”œβ”€β”€ components/           # UI components
β”‚   β”œβ”€β”€ BlockList.vue
β”‚   β”œβ”€β”€ BlockItem.vue
β”‚   β”œβ”€β”€ ItemSelectorDrawer.vue
β”‚   β”œβ”€β”€ ItemSelectorTable.vue
β”‚   └── ...
β”œβ”€β”€ composables/          # Business logic
β”‚   β”œβ”€β”€ useExpandableBlocks.ts
β”‚   β”œβ”€β”€ useBlockState.ts
β”‚   β”œβ”€β”€ useBlockActions.ts
β”‚   β”œβ”€β”€ useBlockWatchers.ts
β”‚   β”œβ”€β”€ useM2AData.ts
β”‚   β”œβ”€β”€ useUIHelpers.ts
β”‚   └── useItemSelector.ts
β”œβ”€β”€ api/                  # Backend API
β”‚   β”œβ”€β”€ api.ts           # Main endpoint
β”‚   β”œβ”€β”€ services/        # Business services
β”‚   └── utils/           # API helpers
β”œβ”€β”€ utils/                # Helpers
└── types/                # TypeScript types

πŸ§ͺ Testing

Unit Tests

describe('useBlockState', () => {
  it('should track dirty state', () => {
    const state = useBlockState(context);
    // Test implementation
  });
});

Integration Tests

// Test API services
describe('ItemLoader Service', () => {
  it('should load items with permissions', async () => {
    // Test implementation
  });
});

πŸ’‘ Key Concepts

1. Timing is Critical

// ❌ Wrong
onMounted(() => {
  processData(props.value); // Often null!
});

// βœ… Correct
watch(() => props.value, (newValue) => {
  if (newValue) processData(newValue);
}, { immediate: true });

2. Composables Pattern

Each composable has a specific responsibility:

  • useBlockState - State management
  • useBlockActions - CRUD operations
  • useBlockWatchers - Reactive effects
  • useM2AData - Data transformation
  • useUIHelpers - UI logic
  • useItemSelector - Item selection logic

3. Dirty State Tracking

The extension tracks both:

  • Content changes (deep comparison)
  • Position changes (order tracking)

4. Logging System

// ❌ Never use console.log directly!
console.log('Debug:', data);

// βœ… Always use the logger wrapper
import { logDebug } from '../utils/logger-wrapper';
logDebug('Debug data', { data });

πŸš€ Performance Tips

  1. Debounce Updates - Prevent excessive emits
  2. Selective Emitting - Only send changed blocks
  3. Lazy Loading - Load block content on expand
  4. Cleanup - Clear watchers and state on unmount

Caching

The extension includes a complete caching infrastructure:

  • Currently DISABLED for development/testing
  • To Enable: Uncomment line 72 in src/api/middleware/cache.ts
  • Benefits: Significantly improves performance for large datasets
  • Configuration: Via environment variables (see Configuration#cache-configuration)

πŸ”’ Security

  • Always validate input
  • Check permissions before actions
  • Never use v-html with user content
  • Let Directus handle validation

πŸ“ TypeScript Type System

The extension is built with TypeScript in strict mode. All types are in src/types/:

src/types/
β”œβ”€β”€ index.ts              # Main exports
β”œβ”€β”€ composable-context.ts # Composable context interfaces
β”œβ”€β”€ data.ts              # Data structure types
β”œβ”€β”€ directus.ts          # Directus-specific types
β”œβ”€β”€ options.ts           # Configuration options
β”œβ”€β”€ props.ts             # Component props
β”œβ”€β”€ relations.ts         # Relationship types
└── translations.ts      # Translation-related types

Key Interfaces

// Main junction record structure
interface JunctionRecord {
  id: string | number;
  collection: string;
  item: string | number | ItemRecord;
  sort?: number;
  [foreignKey: string]: any;
}

// Composable context
interface ExpandableBlocksContext {
  props: Ref<ExpandableBlocksProps>;
  emit: (event: string, ...args: any[]) => void;
  stores: DirectusStores;
}

πŸ§ͺ Testing Strategy

Unit Testing with Vitest

npm run test -- --run     # Run once
npm run test:ui           # UI interface
npm run test:coverage     # Coverage report

Test Structure:

test/
β”œβ”€β”€ unit/
β”‚   β”œβ”€β”€ composables/     # Composable tests
β”‚   β”œβ”€β”€ components/      # Component tests
β”‚   β”œβ”€β”€ utils/          # Utility tests
β”‚   └── api/            # API service tests
β”œβ”€β”€ mocks/              # Mocked dependencies
└── setup.ts           # Test configuration

Coverage Goals:

  • Statements: > 80%
  • Branches: > 75%
  • Functions: > 80%
  • Lines: > 80%

Testing Best Practices

  1. Test Organization
describe('Feature: Drag and Drop', () => {
  describe('when sorting enabled', () => {
    // Tests
  });
});
  1. Use Factories
function createBlock(overrides = {}) {
  return {
    id: 1,
    collection: 'content_text',
    item: { title: 'Test' },
    ...overrides
  };
}

πŸ“‹ Logging Guidelines

⚠️ NEVER use console.log directly!

Always use the logger wrapper:

// ❌ Wrong
console.log('Debug:', data);

// βœ… Correct
import { logDebug, logError, logWarn } from '../utils/logger-wrapper';
logDebug('Debug data', { data });

Logging Behavior

  • Development Mode: Logging is automatically enabled
  • Production Mode: Logging is disabled by default
  • Manual Control in Production:
    // Enable logging in browser console
    logger.enable();  // or localStorage.setItem('EXPANDABLE_BLOCKS_DEBUG', 'true')
    
    // Disable logging
    logger.disable();
    
    // Check status
    logger.isEnabled();

Logger Functions

  • logDebug() - Debug information
  • logError() - Errors with stack traces (always in development)
  • logWarn() - Warnings
  • logAction() - User actions
  • logStateChange() - State changes
  • logEvent() - Events
  • logPerformance() - Performance metrics

Scoped Loggers

import { createScopedLogger } from '../utils/logger-wrapper';
const logger = createScopedLogger('MyComponent');

logger.log('Opening drawer', { collection });
logger.error('Failed to load', error);

🎬 Demo Video Recording

The extension includes a professional demo recording system using Playwright:

Creating Demo Videos

npm run demo:product      # Complete presentation
npm run demo:highlights   # Feature highlights
npm run demo:record       # All demos

Output:

  • WebM Videos: demo-test-results/
  • Screenshots: demo-test-results/*/test-finished-*.png
  • HTML Report: demo-results/index.html

πŸ“š Related Documentation

Back to: Home

Directus Expandable Blocks

🏠 Getting Started

πŸ“– Technical Documentation

πŸ› οΈ Development

πŸ“‹ Project Info

πŸ”— Quick Links


GitHub release

Clone this wiki locally