Files
dees-wcctools/ts_demotools/readme.md

3.9 KiB

@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:

pnpm add -D @design.estate/dees-wcctools

Usage

Import the demotools subpath:

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

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

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

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

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:

: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.