feat(recording-panel): Add demo wrapper utilities, improve recording trim behavior, and harden property panel element detection; update documentation
This commit is contained in:
147
ts_demotools/readme.md
Normal file
147
ts_demotools/readme.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# @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<void>` | Callback executed after content renders |
|
||||
|
||||
### Example: Basic Usage
|
||||
|
||||
```typescript
|
||||
import { html } from 'lit';
|
||||
import '@design.estate/dees-wcctools/demotools';
|
||||
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${(wrapper) => {
|
||||
const button = wrapper.querySelector('my-button');
|
||||
console.log('Button found:', button);
|
||||
}}>
|
||||
<my-button>Click Me</my-button>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Async Operations
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${async (wrapper) => {
|
||||
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);
|
||||
}}>
|
||||
<my-form></my-form>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Multiple Elements
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${(wrapper) => {
|
||||
// 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!'));
|
||||
});
|
||||
}}>
|
||||
<my-card title="Card 1"></my-card>
|
||||
<my-card title="Card 2"></my-card>
|
||||
<my-card title="Card 3"></my-card>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
### Example: Component State Manipulation
|
||||
|
||||
```typescript
|
||||
public static demo = () => html`
|
||||
<dees-demowrapper .runAfterRender=${async (wrapper) => {
|
||||
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);
|
||||
}}>
|
||||
<my-tabs>
|
||||
<div slot="home">Home Content</div>
|
||||
<div slot="settings">Settings Content</div>
|
||||
</my-tabs>
|
||||
</dees-demowrapper>
|
||||
`;
|
||||
```
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user