@adaas/are-html 0.0.23 → 0.0.25
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/dist/browser/index.d.mts +18 -2
- package/dist/browser/index.mjs +35 -10
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +17 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +17 -1
- package/dist/node/directives/AreDirectiveIf.directive.js +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.mjs +29 -6
- package/dist/node/directives/AreDirectiveIf.directive.mjs.map +1 -1
- package/dist/node/engine/AreHTML.compiler.d.mts +3 -1
- package/dist/node/engine/AreHTML.compiler.d.ts +3 -1
- package/dist/node/engine/AreHTML.compiler.js +7 -4
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +7 -4
- package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
- package/examples/for-perf/dist/index.html +1 -1
- package/examples/for-perf/dist/{mqj1mpf2-z4aokv.js → mqp8i2py-vltsx0.js} +2488 -2373
- package/examples/lazy-loading/README.md +76 -0
- package/examples/lazy-loading/concept.ts +55 -0
- package/examples/lazy-loading/containers/UI.container.ts +215 -0
- package/examples/lazy-loading/dist/app.js +3803 -0
- package/examples/{for-perf/dist/mqj1mpff-4fr7mw.js → lazy-loading/dist/chunks/chunk-6K72IBO4.js} +2688 -5897
- package/examples/lazy-loading/dist/index.html +36 -0
- package/examples/lazy-loading/dist/lazy/about-page.js +59 -0
- package/examples/lazy-loading/dist/lazy/reports-page.js +65 -0
- package/examples/lazy-loading/dist/lazy/settings-page.js +54 -0
- package/examples/lazy-loading/public/index.html +36 -0
- package/examples/lazy-loading/src/components/AppShell.component.ts +44 -0
- package/examples/lazy-loading/src/components/HomePage.component.ts +59 -0
- package/examples/lazy-loading/src/components/LazyOutlet.component.ts +108 -0
- package/examples/lazy-loading/src/components/NavBar.component.ts +98 -0
- package/examples/lazy-loading/src/concept.ts +116 -0
- package/examples/lazy-loading/src/lazy/AboutPage.component.ts +54 -0
- package/examples/lazy-loading/src/lazy/ReportsPage.component.ts +56 -0
- package/examples/lazy-loading/src/lazy/SettingsPage.component.ts +45 -0
- package/examples/lazy-loading/src/runtime/ComponentManifest.fragment.ts +61 -0
- package/examples/lazy-loading/src/runtime/LazyComponentResolver.fragment.ts +77 -0
- package/examples/os-desktop/README.md +91 -0
- package/examples/os-desktop/concept.ts +54 -0
- package/examples/os-desktop/containers/OS.container.ts +198 -0
- package/examples/os-desktop/containers/apps/AppBackend.ts +29 -0
- package/examples/os-desktop/containers/apps/GanttApp.backend.ts +56 -0
- package/examples/os-desktop/containers/apps/MarketingApp.backend.ts +68 -0
- package/examples/os-desktop/dist/app.js +4410 -0
- package/examples/os-desktop/dist/apps/gantt/app.js +271 -0
- package/examples/os-desktop/dist/apps/marketing/app.js +346 -0
- package/examples/os-desktop/dist/chunks/chunk-6K72IBO4.js +12455 -0
- package/examples/os-desktop/dist/chunks/chunk-EIIGUL6N.js +30 -0
- package/examples/os-desktop/dist/chunks/chunk-WOH7L5UR.js +30 -0
- package/examples/os-desktop/dist/index.html +33 -0
- package/examples/os-desktop/public/index.html +33 -0
- package/examples/os-desktop/src/apps/gantt/GanttApp.component.ts +41 -0
- package/examples/os-desktop/src/apps/gantt/GanttChart.component.ts +126 -0
- package/examples/os-desktop/src/apps/gantt/GanttStore.ts +47 -0
- package/examples/os-desktop/src/apps/gantt/GanttToolbar.component.ts +73 -0
- package/examples/os-desktop/src/apps/gantt/index.ts +13 -0
- package/examples/os-desktop/src/apps/marketing/MarketingApp.component.ts +53 -0
- package/examples/os-desktop/src/apps/marketing/MarketingStore.ts +34 -0
- package/examples/os-desktop/src/apps/marketing/PostEditor.component.ts +153 -0
- package/examples/os-desktop/src/apps/marketing/PostPreview.component.ts +110 -0
- package/examples/os-desktop/src/apps/marketing/index.ts +16 -0
- package/examples/os-desktop/src/concept.ts +126 -0
- package/examples/os-desktop/src/os/AppStage.component.ts +112 -0
- package/examples/os-desktop/src/os/AppWindow.component.ts +102 -0
- package/examples/os-desktop/src/os/Desktop.component.ts +106 -0
- package/examples/os-desktop/src/os/Dock.component.ts +174 -0
- package/examples/os-desktop/src/os/Hud.component.ts +83 -0
- package/examples/os-desktop/src/os/Launchpad.component.ts +191 -0
- package/examples/os-desktop/src/os/MenuBar.component.ts +156 -0
- package/examples/os-desktop/src/runtime/AppComponentResolver.fragment.ts +121 -0
- package/examples/os-desktop/src/runtime/AppRegistry.fragment.ts +104 -0
- package/examples/os-desktop/src/signals/MouseState.signal.ts +34 -0
- package/examples/os-desktop/src/signals/OSRoute.signal.ts +37 -0
- package/examples/os-desktop/src/signals/SelectionState.signal.ts +34 -0
- package/examples/signal-routing/dist/index.html +1 -1
- package/examples/signal-routing/dist/{mqiwo23h-bhcolu.js → mqp8hgce-4d6rh0.js} +2911 -2708
- package/package.json +11 -7
- package/src/directives/AreDirectiveIf.directive.ts +33 -4
- package/src/engine/AreHTML.compiler.ts +12 -2
- package/tests/PropPropagation.test.ts +181 -0
- package/tests/jest.setup.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaas/are-html",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "A-Concept Rendering Engine (ARE) is a powerful rendering engine designed to work seamlessly with the A-Concept framework. This library provides an HTML engine implementation of ARE, enabling developers to create dynamic and interactive user interfaces for web applications using standard HTML syntax.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a-concept",
|
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
"example:jumpstart": "nodemon ./examples/jumpstart/concept.ts",
|
|
72
72
|
"example:dashboard": "nodemon ./examples/dashboard/concept.ts",
|
|
73
73
|
"example:signal-routing": "nodemon ./examples/signal-routing/concept.ts",
|
|
74
|
+
"example:lazy-loading": "nodemon ./examples/lazy-loading/concept.ts",
|
|
75
|
+
"example:os-desktop": "nodemon ./examples/os-desktop/concept.ts",
|
|
74
76
|
"example:component-styles": "nodemon ./examples/component-styles/concept.ts",
|
|
75
77
|
"example:auxta": "nodemon ./examples/auxta/concept.ts",
|
|
76
78
|
"example:for-perf": "nodemon ./examples/for-perf/concept.ts",
|
|
@@ -83,16 +85,16 @@
|
|
|
83
85
|
"build": "tsup --config tsup.config.ts"
|
|
84
86
|
},
|
|
85
87
|
"peerDependencies": {
|
|
86
|
-
"@adaas/a-concept": "^0.3.
|
|
88
|
+
"@adaas/a-concept": "^0.3.30",
|
|
87
89
|
"@adaas/a-frame": "^0.1.18",
|
|
88
|
-
"@adaas/a-utils": "^0.3.
|
|
89
|
-
"@adaas/are": "^0.0.
|
|
90
|
+
"@adaas/a-utils": "^0.3.35",
|
|
91
|
+
"@adaas/are": "^0.0.26"
|
|
90
92
|
},
|
|
91
93
|
"devDependencies": {
|
|
92
|
-
"@adaas/a-concept": "^0.3.
|
|
94
|
+
"@adaas/a-concept": "^0.3.30",
|
|
93
95
|
"@adaas/a-frame": "^0.1.18",
|
|
94
|
-
"@adaas/a-utils": "^0.3.
|
|
95
|
-
"@adaas/are": "^0.0.
|
|
96
|
+
"@adaas/a-utils": "^0.3.35",
|
|
97
|
+
"@adaas/are": "^0.0.26",
|
|
96
98
|
"@types/chai": "^4.3.14",
|
|
97
99
|
"@types/jest": "^29.5.12",
|
|
98
100
|
"@types/mocha": "^10.0.6",
|
|
@@ -101,6 +103,8 @@
|
|
|
101
103
|
"chai": "^5.1.0",
|
|
102
104
|
"dotenv": "^16.4.5",
|
|
103
105
|
"jest": "^29.7.0",
|
|
106
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
107
|
+
"jsdom": "^29.1.1",
|
|
104
108
|
"mocha": "^10.4.0",
|
|
105
109
|
"nodemon": "^3.1.4",
|
|
106
110
|
"ts-jest": "^29.1.2",
|
|
@@ -96,9 +96,7 @@ export class AreDirectiveIf extends AreDirective {
|
|
|
96
96
|
* 1. Extract the value from the store based on the attribute content
|
|
97
97
|
* (which is the path to the value in the store)
|
|
98
98
|
*/
|
|
99
|
-
attribute.value =
|
|
100
|
-
...(directiveContext?.scope || {}),
|
|
101
|
-
});
|
|
99
|
+
attribute.value = this.evaluateCondition(syntax, attribute, store, directiveContext);
|
|
102
100
|
|
|
103
101
|
/**
|
|
104
102
|
* 2. If the value is falsy, remove the node from the scene by planning a RemoveElement instruction.
|
|
@@ -127,6 +125,7 @@ export class AreDirectiveIf extends AreDirective {
|
|
|
127
125
|
@A_Inject(A_Scope) scope: A_Scope,
|
|
128
126
|
@A_Inject(AreSyntax) syntax: AreSyntax,
|
|
129
127
|
@A_Inject(AreScene) scene: AreScene,
|
|
128
|
+
@A_Inject(AreDirectiveContext) directiveContext?: AreDirectiveContext,
|
|
130
129
|
...args: any[]
|
|
131
130
|
): void {
|
|
132
131
|
/**
|
|
@@ -134,7 +133,7 @@ export class AreDirectiveIf extends AreDirective {
|
|
|
134
133
|
* (which is the path to the value in the store)
|
|
135
134
|
*/
|
|
136
135
|
const previous = !!attribute.value;
|
|
137
|
-
const next =
|
|
136
|
+
const next = this.evaluateCondition(syntax, attribute, store, directiveContext);
|
|
138
137
|
attribute.value = next;
|
|
139
138
|
|
|
140
139
|
// Skip when truthiness has not changed — avoids redundant mount/unmount.
|
|
@@ -149,4 +148,34 @@ export class AreDirectiveIf extends AreDirective {
|
|
|
149
148
|
}
|
|
150
149
|
}
|
|
151
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Evaluates the `$if` condition defensively.
|
|
153
|
+
*
|
|
154
|
+
* A condition can reference data that is momentarily unavailable — most
|
|
155
|
+
* commonly a nested `$if` (e.g. `$if="selected.fields.length"`) living
|
|
156
|
+
* inside a parent `$if="selected"` whose object has just become `null`.
|
|
157
|
+
* Because the nested directive is still subscribed to the store, its
|
|
158
|
+
* update fires on that same change and the raw expression would throw
|
|
159
|
+
* `Cannot read properties of null`, crashing the whole update pipeline.
|
|
160
|
+
*
|
|
161
|
+
* Treating an evaluation error as `false` is the correct contract for a
|
|
162
|
+
* conditional: if the condition cannot be resolved, the subtree simply
|
|
163
|
+
* stays hidden until the referenced data is present again (at which point
|
|
164
|
+
* the parent `$if` re-activates and re-evaluates this one).
|
|
165
|
+
*/
|
|
166
|
+
private evaluateCondition(
|
|
167
|
+
syntax: AreSyntax,
|
|
168
|
+
attribute: AreDirectiveAttribute,
|
|
169
|
+
store: AreStore,
|
|
170
|
+
directiveContext?: AreDirectiveContext,
|
|
171
|
+
): boolean {
|
|
172
|
+
try {
|
|
173
|
+
return !!syntax.evaluate(attribute.content, store, {
|
|
174
|
+
...(directiveContext?.scope || {}),
|
|
175
|
+
});
|
|
176
|
+
} catch {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
152
181
|
}
|
|
@@ -15,6 +15,7 @@ import { AddListenerInstruction} from "@adaas/are-html/instructions/AddListener.
|
|
|
15
15
|
import { AddStyleInstruction } from "@adaas/are-html/instructions/AddStyle.instruction";
|
|
16
16
|
import { AddStaticHTMLInstruction } from "@adaas/are-html/instructions/AddStaticHTML.instruction";
|
|
17
17
|
import { AreHTMLNode } from "@adaas/are-html/node";
|
|
18
|
+
import { AreDirectiveContext } from "@adaas/are-html/directive/AreDirective.context";
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
|
|
@@ -219,6 +220,7 @@ export class AreHTMLCompiler extends AreCompiler {
|
|
|
219
220
|
@A_Inject(AreStore) parentStore: AreStore,
|
|
220
221
|
@A_Inject(AreStore) store: AreStore,
|
|
221
222
|
@A_Inject(AreSyntax) syntax: AreSyntax,
|
|
223
|
+
@A_Inject(AreDirectiveContext) directiveContext?: AreDirectiveContext,
|
|
222
224
|
...args: any[]
|
|
223
225
|
) {
|
|
224
226
|
if (!scene.host)
|
|
@@ -262,13 +264,21 @@ export class AreHTMLCompiler extends AreCompiler {
|
|
|
262
264
|
return value;
|
|
263
265
|
};
|
|
264
266
|
|
|
267
|
+
// Item-scoped variables from an enclosing directive (the `item`/`index`
|
|
268
|
+
// of a `$for`, or any scope a `$if` merged in) so a prop binding like
|
|
269
|
+
// `:title="item.name"` resolves the loop variable — checked BEFORE the
|
|
270
|
+
// store, exactly like plain attribute bindings do in the interpreter.
|
|
271
|
+
// Read `.scope` lazily inside each evaluation so keyed `$for` updates
|
|
272
|
+
// that reassign the context's scope are always reflected.
|
|
273
|
+
const directiveScope = () => directiveContext?.scope ?? {};
|
|
274
|
+
|
|
265
275
|
// The watcher entity below is registered against parentStore so that
|
|
266
276
|
// updates to the bound expression in the parent flow into the child store.
|
|
267
277
|
const watcher = {
|
|
268
278
|
update: () => {
|
|
269
279
|
try {
|
|
270
280
|
parentStore.watch(watcher);
|
|
271
|
-
const next = coerce(syntax.evaluate(attribute.content, parentStore));
|
|
281
|
+
const next = coerce(syntax.evaluate(attribute.content, parentStore, directiveScope()));
|
|
272
282
|
parentStore.unwatch(watcher);
|
|
273
283
|
store.set(propName!, next);
|
|
274
284
|
} catch (e) {
|
|
@@ -279,7 +289,7 @@ export class AreHTMLCompiler extends AreCompiler {
|
|
|
279
289
|
|
|
280
290
|
// Initial read with watch active so dependencies are recorded.
|
|
281
291
|
parentStore.watch(watcher);
|
|
282
|
-
const initial = coerce(syntax.evaluate(attribute.content, parentStore));
|
|
292
|
+
const initial = coerce(syntax.evaluate(attribute.content, parentStore, directiveScope()));
|
|
283
293
|
parentStore.unwatch(watcher);
|
|
284
294
|
|
|
285
295
|
store.set(propName, initial);
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
* @jest-environment-options {"customExportConditions": ["node", "require", "default"]}
|
|
4
|
+
*
|
|
5
|
+
* Integration coverage for parent → child PROP propagation via `:prop="expr"`
|
|
6
|
+
* bindings, including the two structural directives `$if` and `$for`.
|
|
7
|
+
*
|
|
8
|
+
* These render a real component tree into a jsdom document through the full
|
|
9
|
+
* ARE pipeline (tokenize → transform → compile → interpret → mount) exactly
|
|
10
|
+
* the way the browser examples bootstrap, then assert on the produced DOM.
|
|
11
|
+
*
|
|
12
|
+
* Scenarios:
|
|
13
|
+
* 1. Plain parent → child prop (literal + store-derived expression).
|
|
14
|
+
* 2. A child prop INSIDE an `$if` block.
|
|
15
|
+
* 3. A child prop INSIDE a `$for` loop that references the loop variable
|
|
16
|
+
* (`:label="item.name"`) — the case that requires the prop-binding
|
|
17
|
+
* compile path to merge the directive (loop) scope.
|
|
18
|
+
* 4. Reactivity: updating the parent store flows into the child prop.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { A_Concept, A_Context } from '@adaas/a-concept';
|
|
22
|
+
import { A_Inject, A_Caller } from '@adaas/a-concept';
|
|
23
|
+
import { A_Config, ConfigReader } from '@adaas/a-utils/a-config';
|
|
24
|
+
import { A_Logger, A_LOGGER_ENV_KEYS } from '@adaas/a-utils/a-logger';
|
|
25
|
+
import { A_SignalBus, A_SignalState } from '@adaas/a-utils/a-signal';
|
|
26
|
+
import { A_Polyfill } from '@adaas/a-utils/a-polyfill';
|
|
27
|
+
|
|
28
|
+
import { Are, AreNode, AreStore, AreContainer, AreInit, AreRoute } from '@adaas/are';
|
|
29
|
+
import { AreRoot } from '@adaas/are-html/root/AreRoot.component';
|
|
30
|
+
import { AreHTMLEngine } from '@adaas/are-html/engine';
|
|
31
|
+
import { AreHTMLEngineContext } from '@adaas/are-html/context';
|
|
32
|
+
import { AreDirectiveIf } from '@adaas/are-html/directives/AreDirectiveIf.directive';
|
|
33
|
+
import { AreDirectiveFor } from '@adaas/are-html/directives/AreDirectiveFor.directive';
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// ── Test components ──────────────────────────────────────────────────────────
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/** Leaf component: renders whatever `label` prop it receives. */
|
|
41
|
+
class TestChild extends Are {
|
|
42
|
+
props: Record<string, any> = {
|
|
43
|
+
label: { type: 'string', default: '' },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
@Are.Template
|
|
47
|
+
template(@A_Inject(A_Caller) node: AreNode): void {
|
|
48
|
+
node.setContent(`<span class="child">{{label}}</span>`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Are.Data
|
|
52
|
+
data(@A_Inject(AreStore) store: AreStore): void {
|
|
53
|
+
store.set({ label: store.get('label') ?? '' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Parent: passes props down plainly, inside `$if`, and inside `$for`. */
|
|
58
|
+
class TestParent extends Are {
|
|
59
|
+
@Are.Template
|
|
60
|
+
template(@A_Inject(A_Caller) node: AreNode): void {
|
|
61
|
+
node.setContent(`
|
|
62
|
+
<div class="parent">
|
|
63
|
+
<test-child class="basic" :label="topLabel"></test-child>
|
|
64
|
+
|
|
65
|
+
<div class="if-wrap" $if="show">
|
|
66
|
+
<test-child class="in-if" :label="topLabel"></test-child>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<test-child class="in-for"
|
|
70
|
+
$for="item in items track item.id"
|
|
71
|
+
:label="item.name"></test-child>
|
|
72
|
+
</div>
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Are.Data
|
|
77
|
+
data(@A_Inject(AreStore) store: AreStore): void {
|
|
78
|
+
store.set({
|
|
79
|
+
topLabel: 'Hello',
|
|
80
|
+
show: true,
|
|
81
|
+
items: [
|
|
82
|
+
{ id: 1, name: 'Alpha' },
|
|
83
|
+
{ id: 2, name: 'Beta' },
|
|
84
|
+
{ id: 3, name: 'Gamma' },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
// ── Harness ──────────────────────────────────────────────────────────────────
|
|
93
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
const flush = () => new Promise<void>((r) => setTimeout(r, 0));
|
|
96
|
+
|
|
97
|
+
async function bootstrap(): Promise<A_Concept> {
|
|
98
|
+
// The engine context reads `container.body.innerHTML` as its source at
|
|
99
|
+
// construction time, so the markup must be in the DOM BEFORE we build it.
|
|
100
|
+
document.body.innerHTML = `<are-root id="app"><test-parent></test-parent></are-root>`;
|
|
101
|
+
|
|
102
|
+
const container = new AreContainer({
|
|
103
|
+
name: 'ARE Prop-Propagation Test',
|
|
104
|
+
components: [
|
|
105
|
+
TestParent,
|
|
106
|
+
TestChild,
|
|
107
|
+
AreDirectiveIf,
|
|
108
|
+
AreDirectiveFor,
|
|
109
|
+
A_SignalBus,
|
|
110
|
+
AreRoot,
|
|
111
|
+
ConfigReader,
|
|
112
|
+
AreHTMLEngine,
|
|
113
|
+
A_Logger,
|
|
114
|
+
],
|
|
115
|
+
entities: [AreInit, AreRoute],
|
|
116
|
+
fragments: [
|
|
117
|
+
new A_SignalState([AreRoute]),
|
|
118
|
+
new AreHTMLEngineContext({ container: document }),
|
|
119
|
+
new A_Config({
|
|
120
|
+
defaults: { [A_LOGGER_ENV_KEYS.LOG_LEVEL]: 'error' },
|
|
121
|
+
}),
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const concept = new A_Concept({
|
|
126
|
+
name: 'adaas-are-html-prop-propagation-test',
|
|
127
|
+
fragments: [
|
|
128
|
+
new A_Config({
|
|
129
|
+
variables: ['CONFIG_VERBOSE', 'DEV_MODE'] as const,
|
|
130
|
+
defaults: { CONFIG_VERBOSE: false, DEV_MODE: false },
|
|
131
|
+
}),
|
|
132
|
+
],
|
|
133
|
+
components: [A_Logger, ConfigReader, A_Polyfill],
|
|
134
|
+
containers: [container],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await concept.load();
|
|
138
|
+
await concept.start();
|
|
139
|
+
await flush();
|
|
140
|
+
|
|
141
|
+
return concept;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
A_Context.reset();
|
|
146
|
+
document.body.innerHTML = '';
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
152
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
describe('Prop propagation — parent → child via :prop', () => {
|
|
155
|
+
|
|
156
|
+
it('passes a store-derived prop to a plain child', async () => {
|
|
157
|
+
await bootstrap();
|
|
158
|
+
|
|
159
|
+
const basic = document.querySelector('.basic .child');
|
|
160
|
+
expect(basic).not.toBeNull();
|
|
161
|
+
expect(basic!.textContent).toBe('Hello');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('passes a prop to a child rendered inside an $if block', async () => {
|
|
165
|
+
await bootstrap();
|
|
166
|
+
|
|
167
|
+
const inIf = document.querySelector('.in-if .child');
|
|
168
|
+
expect(inIf).not.toBeNull();
|
|
169
|
+
expect(inIf!.textContent).toBe('Hello');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('passes the loop variable as a prop to children inside a $for', async () => {
|
|
173
|
+
await bootstrap();
|
|
174
|
+
|
|
175
|
+
const forChildren = Array.from(
|
|
176
|
+
document.querySelectorAll('.in-for .child'),
|
|
177
|
+
).map((el) => el.textContent);
|
|
178
|
+
|
|
179
|
+
expect(forChildren).toEqual(['Alpha', 'Beta', 'Gamma']);
|
|
180
|
+
});
|
|
181
|
+
});
|
package/tests/jest.setup.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
// jsdom test environments do not expose Node's TextEncoder/TextDecoder globals,
|
|
2
|
+
// which the @adaas codec layer requires. Polyfill them when missing (no-op under
|
|
3
|
+
// the default `node` test environment where they already exist).
|
|
4
|
+
import { TextEncoder, TextDecoder } from 'util';
|
|
5
|
+
if (typeof (globalThis as any).TextEncoder === 'undefined') {
|
|
6
|
+
(globalThis as any).TextEncoder = TextEncoder;
|
|
7
|
+
}
|
|
8
|
+
if (typeof (globalThis as any).TextDecoder === 'undefined') {
|
|
9
|
+
(globalThis as any).TextDecoder = TextDecoder;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
// import { A_Context } from '@adaas/a-concept/a-context';
|
|
2
13
|
// import fs from 'fs';
|
|
3
14
|
|