lego-dom 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ignore/auto.html +135 -0
- package/.ignore/test.html +73 -0
- package/about.md +523 -0
- package/index.js +26 -0
- package/main.js +459 -0
- package/main.test.js +86 -0
- package/package.json +14 -8
- package/README.md +0 -175
- package/dist/dom/index.js +0 -1
- package/dist/index.js +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/traverser.js +0 -1
- package/dist/veyors/basket.js +0 -2
- package/dist/veyors/brick.js +0 -1
- package/dist/veyors/index.js +0 -1
- package/dist/veyors/router.js +0 -1
- package/example/blocks/banner.lego +0 -0
- package/example/blocks/card.lego +0 -40
- package/example/blocks/form.lego +0 -31
- package/example/bricks/index.html +0 -50
- package/src/dom/index.ts +0 -0
- package/src/index.ts +0 -0
- package/src/utils/index.ts +0 -0
- package/src/utils/traverser.ts +0 -0
- package/src/veyors/basket.ts +0 -5
- package/src/veyors/brick.ts +0 -0
- package/src/veyors/index.ts +0 -0
- package/src/veyors/router.ts +0 -0
- package/tsconfig.json +0 -69
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Lego JS | Test Suite</title>
|
|
6
|
+
<script src="./main.js"></script>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: system-ui; line-height: 1.5; padding: 2rem; background: #f4f4f9; }
|
|
9
|
+
.test-card { background: white; padding: 1rem; margin-bottom: 1rem; border-radius: 8px; border-left: 5px solid #ccc; shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
10
|
+
.pass { border-left-color: #22c55e; }
|
|
11
|
+
.fail { border-left-color: #ef4444; background: #fee2e2; }
|
|
12
|
+
.hidden { display: none; }
|
|
13
|
+
h1 { color: #1e293b; }
|
|
14
|
+
.stats { margin-bottom: 2rem; font-weight: bold; }
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
|
|
19
|
+
<h1>Lego JS Test Runner</h1>
|
|
20
|
+
<div id="stats" class="stats">Running tests...</div>
|
|
21
|
+
<div id="test-results"></div>
|
|
22
|
+
|
|
23
|
+
<!-- Test Sandbox (Hidden) -->
|
|
24
|
+
<div id="sandbox" class="hidden"></div>
|
|
25
|
+
|
|
26
|
+
<!-- Test Templates -->
|
|
27
|
+
<template lego-block="test-basic">
|
|
28
|
+
<span id="output">{{text}}</span>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<template lego-block="test-events">
|
|
32
|
+
<button id="btn" @click="count++">Click</button>
|
|
33
|
+
<input id="input" @keyup="lastUsedKey = event.key">
|
|
34
|
+
<span id="display">{{count}}</span>
|
|
35
|
+
<span id="key-display">{{lastUsedKey}}</span>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<template lego-block="test-loops">
|
|
39
|
+
<div l-for="item in items">
|
|
40
|
+
<span class="item">{{item.name}}</span>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
const resultsContainer = document.getElementById('test-results');
|
|
46
|
+
const sandbox = document.getElementById('sandbox');
|
|
47
|
+
const statsEl = document.getElementById('stats');
|
|
48
|
+
let passed = 0;
|
|
49
|
+
let failed = 0;
|
|
50
|
+
|
|
51
|
+
function assert(condition, message) {
|
|
52
|
+
const card = document.createElement('div');
|
|
53
|
+
card.className = 'test-card ' + (condition ? 'pass' : 'fail');
|
|
54
|
+
card.innerHTML = `<strong>${condition ? 'PASS' : 'FAIL'}</strong>: ${message}`;
|
|
55
|
+
resultsContainer.appendChild(card);
|
|
56
|
+
if (condition) passed++; else failed++;
|
|
57
|
+
updateStats();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function updateStats() {
|
|
61
|
+
statsEl.textContent = `Total: ${passed + failed} | Passed: ${passed} | Failed: ${failed}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function wait(ms = 50) {
|
|
65
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- TEST SUITE ---
|
|
69
|
+
|
|
70
|
+
async function runTests() {
|
|
71
|
+
// 1. Basic Reactivity & Interpolation
|
|
72
|
+
const t1 = document.createElement('test-basic');
|
|
73
|
+
t1.setAttribute('l-studs', "{ text: 'Initial' }");
|
|
74
|
+
sandbox.appendChild(t1);
|
|
75
|
+
await wait();
|
|
76
|
+
|
|
77
|
+
const out1 = t1.shadowRoot.getElementById('output');
|
|
78
|
+
assert(out1.textContent === 'Initial', 'Initial text interpolation works');
|
|
79
|
+
|
|
80
|
+
t1._studs.text = 'Updated';
|
|
81
|
+
await wait(); // Wait for requestAnimationFrame batching
|
|
82
|
+
assert(out1.textContent === 'Updated', 'Reactive update reflects in DOM');
|
|
83
|
+
|
|
84
|
+
// 2. Dynamic Event Listeners (@click, @keyup)
|
|
85
|
+
const t2 = document.createElement('test-events');
|
|
86
|
+
t2.setAttribute('l-studs', "{ count: 0, lastUsedKey: '' }");
|
|
87
|
+
sandbox.appendChild(t2);
|
|
88
|
+
await wait();
|
|
89
|
+
|
|
90
|
+
const btn = t2.shadowRoot.getElementById('btn');
|
|
91
|
+
const display = t2.shadowRoot.getElementById('display');
|
|
92
|
+
btn.click();
|
|
93
|
+
await wait();
|
|
94
|
+
assert(display.textContent === '1', 'Universal @click listener works');
|
|
95
|
+
|
|
96
|
+
const input = t2.shadowRoot.getElementById('input');
|
|
97
|
+
const keyDisplay = t2.shadowRoot.getElementById('key-display');
|
|
98
|
+
input.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
|
|
99
|
+
await wait();
|
|
100
|
+
assert(keyDisplay.textContent === 'Enter', 'Universal @keyup listener works');
|
|
101
|
+
|
|
102
|
+
// 3. List Rendering (l-for)
|
|
103
|
+
const t3 = document.createElement('test-loops');
|
|
104
|
+
t3.setAttribute('l-studs', "{ items: [{name: 'A'}, {name: 'B'}] }");
|
|
105
|
+
sandbox.appendChild(t3);
|
|
106
|
+
await wait();
|
|
107
|
+
|
|
108
|
+
let items = t3.shadowRoot.querySelectorAll('.item');
|
|
109
|
+
assert(items.length === 2 && items[0].textContent === 'A', 'l-for renders initial list');
|
|
110
|
+
|
|
111
|
+
t3._studs.items.push({name: 'C'});
|
|
112
|
+
await wait();
|
|
113
|
+
items = t3.shadowRoot.querySelectorAll('.item');
|
|
114
|
+
assert(items.length === 3, 'l-for reactively handles array push');
|
|
115
|
+
|
|
116
|
+
// 4. Security: HTML Escaping
|
|
117
|
+
const t4 = document.createElement('test-basic');
|
|
118
|
+
t4.setAttribute('l-studs', "{ text: '<img src=x onerror=alert(1)>' }");
|
|
119
|
+
sandbox.appendChild(t4);
|
|
120
|
+
await wait();
|
|
121
|
+
const out4 = t4.shadowRoot.getElementById('output');
|
|
122
|
+
assert(out4.innerHTML.includes('<img'), 'HTML is properly escaped to prevent XSS');
|
|
123
|
+
|
|
124
|
+
// 5. Lego.define (Programmatic API)
|
|
125
|
+
Lego.define('js-comp', '<h1>JS Component</h1>');
|
|
126
|
+
const t5 = document.createElement('js-comp');
|
|
127
|
+
sandbox.appendChild(t5);
|
|
128
|
+
await wait();
|
|
129
|
+
assert(t5.shadowRoot.querySelector('h1').textContent === 'JS Component', 'Lego.define works programmatically');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
window.onload = runTests;
|
|
133
|
+
</script>
|
|
134
|
+
</body>
|
|
135
|
+
</html>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset="UTF-8">
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
|
+
<title>Lego JS Test</title>
|
|
6
|
+
<script src="./main.js"></script>
|
|
7
|
+
</head>
|
|
8
|
+
<body @todo-added="() => {console.log('Todo added!')}" >
|
|
9
|
+
<template b-id="todo-list">
|
|
10
|
+
<style>
|
|
11
|
+
:host { display: block; font-family: system-ui, sans-serif; padding: 20px; }
|
|
12
|
+
.done { text-decoration: line-through; color: #888; }
|
|
13
|
+
.input { padding: .4em; border: 1px solid #ccc; border-radius: 0.1em; }
|
|
14
|
+
.item-row { margin-bottom: 8px; display: flex; align-items: center; gap: 10px; }
|
|
15
|
+
button { cursor: pointer; border: none; padding: 5px 10px; border-radius: 0px; background-color: #404; color: #fff; }
|
|
16
|
+
h3 { color: #318d8d; margin-top: 0; }
|
|
17
|
+
</style>
|
|
18
|
+
|
|
19
|
+
<h3>{{title}}</h3>
|
|
20
|
+
<p>{{ items.filter(t => !t.done).length }} of {{ items.length }} items remaining</p>
|
|
21
|
+
<div style="margin-bottom: 20px;">
|
|
22
|
+
<input b-sync="newItem" placeholder="Add task..." class="input" @keydown="() => {
|
|
23
|
+
}">
|
|
24
|
+
<button @click="() => {
|
|
25
|
+
if(!newItem.trim()) return;
|
|
26
|
+
items.push({text: `${newItem} - ${items.length + 1} - ${$ancestors('user-card').name}`, done: false});
|
|
27
|
+
newItem = '';
|
|
28
|
+
$emit('todo-added', { item: newItem });
|
|
29
|
+
}">Add</button>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div b-for="todo in items">
|
|
33
|
+
<div class="item-row {{todo.done ? 'done' : ''}}">
|
|
34
|
+
<input type="checkbox" b-sync="todo.done">
|
|
35
|
+
<span>{{todo.text}}</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<template b-id="user-card">
|
|
41
|
+
<style>
|
|
42
|
+
:host { display: block; font-family: system-ui, sans-serif; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
|
|
43
|
+
h3 { color: #62318d; margin-top: 0; }
|
|
44
|
+
</style>
|
|
45
|
+
|
|
46
|
+
<h3>{{name}}</h3>
|
|
47
|
+
<p>{{bio}}</p>
|
|
48
|
+
<slot></slot>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<user-card b-data="{
|
|
52
|
+
name: 'John Doe',
|
|
53
|
+
bio: 'Software Engineer'
|
|
54
|
+
}">
|
|
55
|
+
<p>Additional content here</p>
|
|
56
|
+
<todo-list b-data="{
|
|
57
|
+
title: 'Enterprise Todo List',
|
|
58
|
+
items: [{text: 'Fix b-for strikethrough', done: true}],
|
|
59
|
+
newItem: '',
|
|
60
|
+
mounted() {
|
|
61
|
+
console.log('THis lego block has been mounted!');
|
|
62
|
+
},
|
|
63
|
+
updated() {
|
|
64
|
+
console.log('This lego block has been updated!');
|
|
65
|
+
},
|
|
66
|
+
unmounted() {
|
|
67
|
+
console.log('This lego block has been unmounted!');
|
|
68
|
+
}
|
|
69
|
+
}">
|
|
70
|
+
</todo-list>
|
|
71
|
+
</user-card>
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|