# @design.estate/dees-wcctools/demotools ๐Ÿงช **Demo Wrapper Utilities** โ€” Enhanced testing tools for web component demos ## Overview The demotools module provides `dees-demowrapper`, a utility component for executing post-render logic in component demos. Perfect for simulating user interactions, setting up test data, or validating component state. ## Installation This module is included with `@design.estate/dees-wcctools`: ```bash pnpm add -D @design.estate/dees-wcctools ``` ## Usage Import the demotools subpath: ```typescript import '@design.estate/dees-wcctools/demotools'; ``` ## DeesDemoWrapper A wrapper component that executes a callback after its slotted content renders. ### Properties | Property | Type | Description | |----------|------|-------------| | `runAfterRender` | `(wrapper: DeesDemoWrapper) => void \| Promise` | Callback executed after content renders | ### Example: Basic Usage ```typescript import { html } from 'lit'; import '@design.estate/dees-wcctools/demotools'; public static demo = () => html` { const button = wrapper.querySelector('my-button'); console.log('Button found:', button); }}> Click Me `; ``` ### Example: Async Operations ```typescript public static demo = () => html` { const form = wrapper.querySelector('my-form'); // Wait for component initialization await form.updateComplete; // Simulate user input form.values = { name: 'Test User', email: 'test@example.com' }; // Trigger validation await form.validate(); console.log('Form state:', form.isValid); }}> `; ``` ### Example: Multiple Elements ```typescript public static demo = () => html` { // Find all cards const cards = wrapper.querySelectorAll('my-card'); console.log(`Found ${cards.length} cards`); // Access by index Array.from(wrapper.children).forEach((child, i) => { console.log(`Child ${i}:`, child.tagName); }); // Add event listeners wrapper.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', () => console.log('Clicked!')); }); }}> `; ``` ### Example: Component State Manipulation ```typescript public static demo = () => html` { const tabs = wrapper.querySelector('my-tabs'); // Programmatically switch tabs tabs.activeTab = 'settings'; await tabs.updateComplete; // Verify content updated const content = tabs.shadowRoot.querySelector('.tab-content'); console.log('Active content:', content.textContent); }}>
Home Content
Settings Content
`; ``` ## How It Works 1. The wrapper renders its slot content immediately 2. After a brief delay (50ms) to allow slotted content to initialize 3. The `runAfterRender` callback is invoked with the wrapper element 4. You have full DOM API access to query and manipulate children ## Key Features - ๐Ÿ“ฆ **Light DOM Access** โ€” Slotted elements remain accessible via standard DOM APIs - โฑ๏ธ **Async Support** โ€” Return a Promise for async operations - ๐ŸŽฏ **Full DOM API** โ€” Use `querySelector`, `querySelectorAll`, `children`, etc. - ๐Ÿ›ก๏ธ **Error Handling** โ€” Errors in callbacks are caught and logged ## CSS Behavior The wrapper uses `display: contents` so it doesn't affect layout: ```css :host { display: contents; } ``` This means the wrapper is "invisible" in the layout โ€” its children render as if they were direct children of the wrapper's parent.