Compare commits

..

6 Commits

Author SHA1 Message Date
3d1948b93e 1.0.97
Some checks failed
Default (tags) / security (push) Failing after 41s
Default (tags) / test (push) Failing after 37s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-16 16:03:28 +00:00
21b7158a35 fix(properties-panel): enhance element detection and error handling for nested structures 2025-06-16 15:56:12 +00:00
992af2668e 1.0.96
Some checks failed
Default (tags) / security (push) Failing after 42s
Default (tags) / test (push) Failing after 39s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-16 15:05:47 +00:00
0cd84b28b4 fix(package): correct path for demotools export in package.json 2025-06-16 15:05:43 +00:00
0709267bd5 1.0.95
Some checks failed
Default (tags) / security (push) Failing after 45s
Default (tags) / test (push) Failing after 40s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-06-16 14:45:28 +00:00
d4fce8a939 fix(demotools): enhance runAfterRender to provide full DOM API access and improve element selection 2025-06-16 14:45:19 +00:00
12 changed files with 750 additions and 65 deletions

View File

@ -1,11 +1,11 @@
{
"name": "@design.estate/dees-wcctools",
"version": "1.0.94",
"version": "1.0.97",
"private": false,
"description": "A set of web component tools for creating element catalogues, enabling the structured development and documentation of custom elements and pages.",
"exports": {
".": "./dist_ts_web/index.js",
"./demotools": "./dist_ts_demotools"
"./demotools": "./dist_ts_demotools/index.js"
},
"type": "module",
"scripts": {

View File

@ -12,10 +12,16 @@ The properties panel had timing issues detecting rendered elements because:
1. Added a 100ms initial delay to allow render completion
2. Implemented recursive element search that:
- Searches through nested children up to 5 levels deep
- Checks shadow roots of elements
- Handles complex DOM structures
- Checks both light DOM and shadow DOM for all elements
- Handles complex DOM structures generically
- Works with any wrapper elements, not specific to dees-demowrapper
3. Added retry mechanism with up to 5 attempts (200ms between retries)
4. Improved error messages to show retry count
5. Comprehensive error handling:
- Errors in element search don't break the update cycle
- Individual property errors don't prevent other properties from rendering
- scheduleUpdate always completes even if createProperties fails
- Clears warnings and property content appropriately on errors
### Code Flow
1. Dashboard renders element demo into viewport using `render(anonItem.demo(), viewport)`
@ -34,16 +40,22 @@ import * as demoTools from '@design.estate/dees-wcctools/demotools';
// In your demo function:
demo: () => html`
<dees-demowrapper .runAfterRender=${(children: HTMLCollection) => {
// Access all slotted elements
const firstElement = children[0] as HTMLElement;
firstElement.setAttribute('data-demo', 'true');
console.log('All slotted elements:', children);
<dees-demowrapper .runAfterRender=${(wrapper) => {
// Use querySelector for specific elements
const myElement = wrapper.querySelector('my-custom-element');
myElement?.setAttribute('data-demo', 'true');
// Work with multiple elements
Array.from(children).forEach(child => {
console.log('Element rendered:', child);
// Access all children
console.log('All children:', wrapper.children);
// Use querySelectorAll for multiple elements
wrapper.querySelectorAll('div').forEach(div => {
console.log('Found div:', div);
});
// Full DOM API available
const firstChild = wrapper.firstElementChild;
const hasClass = wrapper.querySelector('.my-class');
}}>
<my-custom-element></my-custom-element>
<div>Additional content</div>
@ -53,7 +65,8 @@ demo: () => html`
**Features:**
- Wraps demo elements without affecting layout (uses `display: contents`)
- Provides access to ALL slotted elements via HTMLCollection after mounting
- Provides the wrapper element itself with full DOM API access
- Use querySelector/querySelectorAll for powerful element selection
- Access children via wrapper.children property
- Supports async operations in runAfterRender callback
- Automatically handles timing to ensure elements are fully rendered
- Can handle multiple slotted elements, not just the first one
- Automatically handles timing to ensure elements are fully rendered

View File

@ -183,23 +183,24 @@ import * as demoTools from '@design.estate/dees-wcctools/demotools';
@customElement('my-component')
export class MyComponent extends DeesElement {
public static demo = () => html`
<dees-demowrapper .runAfterRender=${async (children: HTMLCollection) => {
// Access all slotted elements
console.log('Slotted elements:', children.length);
<dees-demowrapper .runAfterRender=${async (wrapper) => {
// Use querySelector to find specific elements
const myComponent = wrapper.querySelector('my-component') as MyComponent;
console.log('Component found:', myComponent);
// Access specific element (e.g., first child)
const myComponent = children[0] as MyComponent;
console.log('Component mounted:', myComponent);
// Access all children via wrapper.children
console.log('Total children:', wrapper.children.length);
// Use querySelectorAll for multiple elements
const allDivs = wrapper.querySelectorAll('div');
console.log('Found divs:', allDivs.length);
// Simulate user interactions
myComponent.value = 'Test value';
await myComponent.updateComplete;
// Trigger methods
myComponent.handleClick();
// Work with multiple elements if present
Array.from(children).forEach((child, index) => {
// Work with all children
Array.from(wrapper.children).forEach((child, index) => {
console.log(`Child ${index}:`, child.tagName);
});
}}>
@ -309,12 +310,18 @@ For complex property types, implement custom logic in your demo:
```typescript
public static demo = () => html`
<dees-demowrapper .runAfterRender=${(children) => {
// Set up complex property handling for all children
Array.from(children).forEach(child => {
child.addEventListener('property-change', (e) => {
<dees-demowrapper .runAfterRender=${(wrapper) => {
// Use querySelector to target specific elements
const component = wrapper.querySelector('my-component');
if (component) {
component.addEventListener('property-change', (e) => {
console.log('Property changed:', e.detail);
});
}
// Or handle all elements of a type
wrapper.querySelectorAll('my-component').forEach(el => {
el.addEventListener('click', () => console.log('Clicked!'));
});
}}>
<my-component></my-component>
@ -354,9 +361,10 @@ Initialize the WCC Tools dashboard.
Component for wrapping demos with post-render logic.
- `runAfterRender`: Function called after the wrapped elements render
- Receives the complete HTMLCollection of all slotted elements
- Receives the wrapper element itself, providing full DOM API access
- Use `wrapper.querySelector()` and `wrapper.querySelectorAll()` for element selection
- Access children via `wrapper.children` property
- Supports async operations
- Access individual elements via array index or Array.from()
## Browser Support
- Chrome/Edge (latest)

View File

@ -55,8 +55,34 @@ The properties panel has timing issues detecting rendered elements because:
# Enhanced DemoWrapper (COMPLETED)
## Modified runAfterRender callback:
- Now receives HTMLCollection of ALL slotted elements instead of just the first one
- Allows access to complete slotted DOM structure
- Now receives the wrapper element itself instead of just children
- Provides full DOM API access (querySelector, querySelectorAll, etc.)
- querySelector works on slotted content (light DOM children)
- Access children via wrapper.children property
- Updated documentation with correct import path (lowercase 'demotools')
- Examples show how to work with multiple slotted elements
- Maintains backward compatibility by accessing elements via index
- Examples show how to use querySelector for powerful element selection
- Added clarifying comment about querySelector working on slotted content
## Fixed Properties Panel Compatibility:
- Made element search generic - works with any container elements
- Searches both light DOM and shadow DOM recursively
- Improved error handling to prevent breaking the update cycle
- Errors in one property don't prevent others from rendering
- Detection continues working even after errors occur
- Maintains compatibility with all element structures
# Test Elements Created (COMPLETED)
## Created comprehensive test elements:
1. **test-noprops** - Element with no @property decorators
2. **test-complextypes** - Element with arrays, objects, dates, and complex nested data
3. **test-withwrapper** - Element that uses dees-demowrapper in its demo
4. **test-edgecases** - Element with edge cases (null, undefined, NaN, Infinity, circular refs)
5. **test-nested** - Element with deeply nested structure to test recursive search
These test various scenarios:
- Properties panel handling of elements without properties
- Complex data type display and editing
- Element detection inside dees-demowrapper
- Error handling for problematic values
- Deep nesting and shadow DOM traversal

View File

@ -1 +1,6 @@
export * from './test-demoelement.js';
export * from './test-noprops.js';
export * from './test-complextypes.js';
export * from './test-withwrapper.js';
export * from './test-edgecases.js';
export * from './test-nested.js';

View File

@ -0,0 +1,137 @@
import {
DeesElement,
customElement,
type TemplateResult,
html,
property,
css,
} from '@design.estate/dees-element';
interface IComplexData {
name: string;
age: number;
tags: string[];
metadata: {
created: Date;
modified: Date;
author: string;
};
}
@customElement('test-complextypes')
export class TestComplexTypes extends DeesElement {
public static demo = () => html`
<test-complextypes
.complexData=${{
name: 'Test User',
age: 25,
tags: ['developer', 'designer'],
metadata: {
created: new Date(),
modified: new Date(),
author: 'System'
}
}}
></test-complextypes>
`;
@property({ type: Array })
public stringArray: string[] = ['apple', 'banana', 'cherry'];
@property({ type: Array })
public numberArray: number[] = [1, 2, 3, 4, 5];
@property({ attribute: false })
public complexData: IComplexData = {
name: 'Default Name',
age: 0,
tags: [],
metadata: {
created: new Date(),
modified: new Date(),
author: 'Unknown'
}
};
@property({ type: Object })
public simpleObject = {
key1: 'value1',
key2: 'value2',
key3: 123
};
@property({ attribute: false })
public functionProperty = () => {
console.log('This is a function property');
};
@property({ type: Date })
public dateProperty = new Date();
public static styles = [
css`
:host {
display: block;
padding: 20px;
background: #f5f5f5;
border: 2px solid #ddd;
border-radius: 8px;
font-family: monospace;
}
.section {
margin: 10px 0;
padding: 10px;
background: white;
border-radius: 4px;
}
.label {
font-weight: bold;
color: #333;
}
.value {
color: #666;
margin-left: 10px;
}
pre {
background: #f0f0f0;
padding: 8px;
border-radius: 4px;
overflow-x: auto;
}
`
];
public render() {
return html`
<div class="section">
<span class="label">String Array:</span>
<span class="value">${this.stringArray.join(', ')}</span>
</div>
<div class="section">
<span class="label">Number Array:</span>
<span class="value">${this.numberArray.join(', ')}</span>
</div>
<div class="section">
<span class="label">Complex Data:</span>
<pre>${JSON.stringify(this.complexData, null, 2)}</pre>
</div>
<div class="section">
<span class="label">Simple Object:</span>
<pre>${JSON.stringify(this.simpleObject, null, 2)}</pre>
</div>
<div class="section">
<span class="label">Date Property:</span>
<span class="value">${this.dateProperty.toLocaleString()}</span>
</div>
<div class="section">
<span class="label">Function Property:</span>
<span class="value">${typeof this.functionProperty}</span>
</div>
`;
}
}

View File

@ -0,0 +1,195 @@
import {
DeesElement,
customElement,
type TemplateResult,
html,
property,
css,
} from '@design.estate/dees-element';
@customElement('test-edgecases')
export class TestEdgeCases extends DeesElement {
public static demo = () => html`<test-edgecases></test-edgecases>`;
// Property with null value
@property({ type: String })
public nullableString: string | null = null;
// Property with undefined value
@property({ type: Number })
public undefinedNumber: number | undefined = undefined;
// Very long string
@property({ type: String })
public longString: string = 'Lorem ipsum '.repeat(50);
// Property with special characters
@property({ type: String })
public specialChars: string = '!@#$%^&*()_+-=[]{}|;\':",./<>?`~';
// Property that could cause rendering issues
@property({ type: String })
public htmlString: string = '<script>alert("test")</script><b>Bold text</b>';
// Numeric edge cases
@property({ type: Number })
public infinityNumber: number = Infinity;
@property({ type: Number })
public nanNumber: number = NaN;
@property({ type: Number })
public veryLargeNumber: number = Number.MAX_SAFE_INTEGER;
@property({ type: Number })
public verySmallNumber: number = Number.MIN_SAFE_INTEGER;
@property({ type: Number })
public floatNumber: number = 3.14159265359;
// Boolean-like values
@property({ type: String })
public booleanString: string = 'false';
@property({ type: Number })
public booleanNumber: number = 0;
// Empty values
@property({ type: String })
public emptyString: string = '';
@property({ type: Array })
public emptyArray: any[] = [];
@property({ type: Object })
public emptyObject: {} = {};
// Circular reference (should not break properties panel)
@property({ attribute: false })
public circularRef: any = (() => {
const obj: any = { name: 'circular' };
obj.self = obj;
return obj;
})();
public static styles = [
css`
:host {
display: block;
padding: 20px;
background: #fff3e0;
border: 2px solid #ff9800;
border-radius: 8px;
font-family: monospace;
font-size: 12px;
}
.warning {
background: #ffe0b2;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
color: #e65100;
}
.property {
margin: 5px 0;
padding: 5px;
background: white;
border-radius: 2px;
word-break: break-all;
}
.label {
font-weight: bold;
color: #f57c00;
}
.value {
color: #666;
}
.special {
background: #ffccbc;
padding: 2px 4px;
border-radius: 2px;
}
`
];
private formatValue(value: any): string {
if (value === null) return 'null';
if (value === undefined) return 'undefined';
if (value === Infinity) return 'Infinity';
if (Number.isNaN(value)) return 'NaN';
if (typeof value === 'string' && value.length > 50) {
return value.substring(0, 50) + '...';
}
if (typeof value === 'object') {
try {
return JSON.stringify(value);
} catch (e) {
return '[Circular Reference]';
}
}
return String(value);
}
public render() {
return html`
<div class="warning">
⚠️ This element tests edge cases and problematic values
</div>
<div class="property">
<span class="label">Nullable String:</span>
<span class="value special">${this.formatValue(this.nullableString)}</span>
</div>
<div class="property">
<span class="label">Undefined Number:</span>
<span class="value special">${this.formatValue(this.undefinedNumber)}</span>
</div>
<div class="property">
<span class="label">Long String:</span>
<span class="value">${this.formatValue(this.longString)}</span>
</div>
<div class="property">
<span class="label">Special Characters:</span>
<span class="value">${this.specialChars}</span>
</div>
<div class="property">
<span class="label">HTML String (escaped):</span>
<span class="value">${this.htmlString}</span>
</div>
<div class="property">
<span class="label">Infinity:</span>
<span class="value special">${this.formatValue(this.infinityNumber)}</span>
</div>
<div class="property">
<span class="label">NaN:</span>
<span class="value special">${this.formatValue(this.nanNumber)}</span>
</div>
<div class="property">
<span class="label">Very Large Number:</span>
<span class="value">${this.veryLargeNumber}</span>
</div>
<div class="property">
<span class="label">Float Number:</span>
<span class="value">${this.floatNumber}</span>
</div>
<div class="property">
<span class="label">Empty String:</span>
<span class="value special">[empty]</span>
</div>
<div class="property">
<span class="label">Circular Reference:</span>
<span class="value special">${this.formatValue(this.circularRef)}</span>
</div>
`;
}
}

View File

@ -0,0 +1,127 @@
import {
DeesElement,
customElement,
type TemplateResult,
html,
property,
css,
} from '@design.estate/dees-element';
// Helper component for nesting
@customElement('test-nested-wrapper')
class TestNestedWrapper extends DeesElement {
public render() {
return html`
<div style="border: 1px dashed #ccc; padding: 10px; margin: 5px;">
<slot></slot>
</div>
`;
}
}
// The actual test element deeply nested
@customElement('test-nested-target')
class TestNestedTarget extends DeesElement {
@property({ type: String })
public message: string = 'I am deeply nested!';
@property({ type: Number })
public depth: number = 0;
@property({ type: Boolean })
public found: boolean = false;
public static styles = [
css`
:host {
display: block;
padding: 15px;
background: #e1f5fe;
border: 2px solid #0288d1;
border-radius: 4px;
margin: 5px;
}
.info {
font-family: monospace;
color: #01579b;
}
`
];
public render() {
return html`
<div class="info">
<strong>Nested Target Element</strong><br>
Message: ${this.message}<br>
Depth: ${this.depth}<br>
Found by properties panel: ${this.found ? '✅' : '❌'}
</div>
`;
}
}
@customElement('test-nested')
export class TestNested extends DeesElement {
public static demo = () => html`
<test-nested></test-nested>
`;
@property({ type: String })
public testId: string = 'nested-test';
public static styles = [
css`
:host {
display: block;
padding: 20px;
background: #f5f5f5;
border: 2px solid #999;
border-radius: 8px;
}
.explanation {
background: #fff;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.structure {
background: #f0f0f0;
padding: 10px;
border-radius: 4px;
}
`
];
public render() {
return html`
<div class="explanation">
<h3>Nested Structure Test</h3>
<p>The actual element with properties is nested deep inside multiple layers:</p>
</div>
<div class="structure">
<test-nested-wrapper>
<div style="padding: 10px; background: #ffe;">
<test-nested-wrapper>
<div style="padding: 10px; background: #efe;">
<test-nested-wrapper>
<div style="padding: 10px; background: #eef;">
<!-- The target element is here, 3 levels deep -->
<test-nested-target
.message=${'Found me at depth 3!'}
.depth=${3}
></test-nested-target>
</div>
</test-nested-wrapper>
</div>
</test-nested-wrapper>
</div>
</test-nested-wrapper>
</div>
<div style="margin-top: 10px; font-style: italic; color: #666;">
Properties panel should find the test-nested-target element despite the deep nesting.
</div>
`;
}
}

View File

@ -0,0 +1,37 @@
import {
DeesElement,
customElement,
type TemplateResult,
html,
css,
} from '@design.estate/dees-element';
@customElement('test-noprops')
export class TestNoProps extends DeesElement {
public static demo = () => html`<test-noprops></test-noprops>`;
public static styles = [
css`
:host {
display: block;
padding: 20px;
background: #f0f0f0;
border: 2px solid #ccc;
border-radius: 8px;
}
.message {
font-family: monospace;
color: #666;
}
`
];
public render() {
return html`
<div class="message">
This element has no @property decorators.
Properties panel should handle this gracefully.
</div>
`;
}
}

View File

@ -0,0 +1,111 @@
import {
DeesElement,
customElement,
type TemplateResult,
html,
property,
css,
} from '@design.estate/dees-element';
// Import from local demotools
import '../../ts_demotools/demotools.js';
@customElement('test-withwrapper')
export class TestWithWrapper extends DeesElement {
public static demo = () => html`
<dees-demowrapper .runAfterRender=${async (wrapper) => {
console.log('DemoWrapper: Found wrapper element', wrapper);
const testElement = wrapper.querySelector('test-withwrapper');
if (testElement) {
console.log('DemoWrapper: Found test-withwrapper element');
testElement.dynamicValue = 'Set by demo wrapper!';
testElement.counter = 100;
// Test querySelector functionality
const innerDiv = wrapper.querySelector('.inner-content');
console.log('DemoWrapper: Found inner div:', innerDiv);
// Test querySelectorAll
const allButtons = wrapper.querySelectorAll('button');
console.log(`DemoWrapper: Found ${allButtons.length} buttons`);
}
}}>
<test-withwrapper></test-withwrapper>
<div style="margin-top: 10px; padding: 10px; background: #e0e0e0;">
This div is also inside the wrapper
</div>
</dees-demowrapper>
`;
@property({ type: String })
public dynamicValue: string = 'Initial value';
@property({ type: Number })
public counter: number = 0;
@property({ type: Boolean })
public isActive: boolean = false;
public static styles = [
css`
:host {
display: block;
padding: 20px;
background: #e8f5e9;
border: 2px solid #4caf50;
border-radius: 8px;
}
.wrapper-info {
background: #c8e6c9;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.inner-content {
background: white;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
button {
background: #4caf50;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
}
button:hover {
background: #45a049;
}
.status {
margin-top: 10px;
font-family: monospace;
}
`
];
public render() {
return html`
<div class="wrapper-info">
This element is wrapped with dees-demowrapper in its demo
</div>
<div class="inner-content">
<h3>Dynamic Value: ${this.dynamicValue}</h3>
<p>Counter: ${this.counter}</p>
<p>Active: ${this.isActive ? 'Yes' : 'No'}</p>
<button @click=${() => this.counter++}>Increment</button>
<button @click=${() => this.isActive = !this.isActive}>Toggle Active</button>
<button @click=${() => this.dynamicValue = 'Clicked!'}>Change Value</button>
</div>
<div class="status">
Properties panel should detect this element inside the wrapper
</div>
`;
}
}

View File

@ -3,7 +3,7 @@ import { DeesElement, customElement, html, css, property, type TemplateResult }
@customElement('dees-demowrapper')
export class DeesDemoWrapper extends DeesElement {
@property({ attribute: false })
public runAfterRender: (children: HTMLCollection) => void | Promise<void>;
public runAfterRender: (wrapperElement: DeesDemoWrapper) => void | Promise<void>;
public static styles = [
css`
@ -25,12 +25,13 @@ export class DeesDemoWrapper extends DeesElement {
// Wait a bit for slotted content to render
await new Promise(resolve => setTimeout(resolve, 50));
// Get all slotted elements
const slottedElements = this.children;
if (slottedElements.length > 0 && this.runAfterRender) {
// Call the runAfterRender function with all slotted elements
// Check if there are slotted elements and runAfterRender is defined
if (this.children.length > 0 && this.runAfterRender) {
// Call the runAfterRender function with the wrapper element itself
// Note: querySelector/querySelectorAll will work on slotted content
// because slotted elements remain in the light DOM as children
try {
await this.runAfterRender(slottedElements);
await this.runAfterRender(this);
} catch (error) {
console.error('Error in runAfterRender:', error);
}

View File

@ -229,23 +229,28 @@ export class WccProperties extends DeesElement {
private async findElementRecursively(container: Element, elementClass: any, maxDepth: number = 5): Promise<HTMLElement | null> {
if (maxDepth <= 0) return null;
// Check direct children
for (const child of Array.from(container.children)) {
if (child instanceof elementClass) {
return child as HTMLElement;
}
}
// Check shadow roots of children
for (const child of Array.from(container.children)) {
if (child.shadowRoot) {
const found = await this.findElementRecursively(child.shadowRoot as any, elementClass, maxDepth - 1);
if (found) return found;
try {
// Check direct children
for (const child of Array.from(container.children)) {
if (child instanceof elementClass) {
return child as HTMLElement;
}
}
// Also check nested children
const found = await this.findElementRecursively(child, elementClass, maxDepth - 1);
if (found) return found;
// Search in all children recursively
for (const child of Array.from(container.children)) {
// First, always check the light DOM children
const found = await this.findElementRecursively(child, elementClass, maxDepth - 1);
if (found) return found;
// Also check shadow root if it exists
if (child.shadowRoot) {
const shadowFound = await this.findElementRecursively(child.shadowRoot as any, elementClass, maxDepth - 1);
if (shadowFound) return shadowFound;
}
}
} catch (error) {
console.error('Error in findElementRecursively:', error);
}
return null;
@ -254,6 +259,9 @@ export class WccProperties extends DeesElement {
public async createProperties() {
console.log('creating properties for:');
console.log(this.selectedItem);
// Clear any previous warnings
this.warning = null;
const isEnumeration = (propertyArg): boolean => {
const keys = Object.keys(propertyArg.type);
const values = [];
@ -315,15 +323,20 @@ export class WccProperties extends DeesElement {
let retries = 0;
while (!firstFoundInstantiatedElement && retries < 5) {
await new Promise(resolve => setTimeout(resolve, 200));
firstFoundInstantiatedElement = await this.findElementRecursively(
viewport,
this.selectedItem as any
);
try {
firstFoundInstantiatedElement = await this.findElementRecursively(
viewport,
this.selectedItem as any
);
} catch (error) {
console.error('Error during element search retry:', error);
}
retries++;
}
if (!firstFoundInstantiatedElement) {
this.warning = `no first instantiated element found for >>${anonItem.name}<< after ${retries} retries`;
this.propertyContent = [];
return;
}
const classProperties: Map<string, any> = anonItem.elementProperties;
@ -337,9 +350,10 @@ export class WccProperties extends DeesElement {
if (key === 'goBright' || key === 'domtools') {
continue;
}
const property = classProperties.get(key);
const propertyTypeString = await determinePropertyType(property);
propertyArray.push(
try {
const property = classProperties.get(key);
const propertyTypeString = await determinePropertyType(property);
propertyArray.push(
html`
<div class="property">
${key} / ${propertyTypeString}<br />
@ -392,6 +406,10 @@ export class WccProperties extends DeesElement {
</div>
`
);
} catch (error) {
console.error(`Error processing property ${key}:`, error);
// Continue with next property even if this one fails
}
}
this.propertyContent = propertyArray;
} else {
@ -413,7 +431,14 @@ export class WccProperties extends DeesElement {
}
public async scheduleUpdate() {
await this.createProperties();
try {
await this.createProperties();
} catch (error) {
console.error('Error creating properties:', error);
// Clear property content on error to show clean state
this.propertyContent = [];
}
// Always call super.scheduleUpdate to ensure component updates
super.scheduleUpdate();
}