@symbiote-native/angular 0.6.1 → 0.7.0
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/README.md +1 -1
- package/bin/symbiote-angular-dev.cjs +20 -19
- package/build/angular/anchor-host-registry.js +2 -2
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/components/activity-indicator/index.android.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.android.js +6 -3
- package/build/angular/components/activity-indicator/index.android.js.map +1 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +2 -1
- package/build/angular/components/activity-indicator/index.ios.js +6 -3
- package/build/angular/components/activity-indicator/index.ios.js.map +1 -1
- package/build/angular/components/activity-indicator/shared.js +3 -3
- package/build/angular/components/button.d.ts +4 -2
- package/build/angular/components/button.js +28 -17
- package/build/angular/components/button.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +5 -3
- package/build/angular/components/flat-list/index.js +35 -38
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image/index.android.d.ts +8 -3
- package/build/angular/components/image/index.android.js +39 -9
- package/build/angular/components/image/index.android.js.map +1 -1
- package/build/angular/components/image/index.ios.d.ts +8 -3
- package/build/angular/components/image/index.ios.js +39 -9
- package/build/angular/components/image/index.ios.js.map +1 -1
- package/build/angular/components/image/shared.d.ts +4 -1
- package/build/angular/components/image/shared.js +33 -2
- package/build/angular/components/image/shared.js.map +1 -1
- package/build/angular/components/image-background.d.ts +2 -1
- package/build/angular/components/image-background.js +6 -4
- package/build/angular/components/image-background.js.map +1 -1
- package/build/angular/components/input-accessory-view/index.d.ts +9 -4
- package/build/angular/components/input-accessory-view/index.js +43 -9
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +2 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +6 -4
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +11 -8
- package/build/angular/components/modal/index.js +45 -28
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable/index.d.ts +11 -5
- package/build/angular/components/pressable/index.js +61 -12
- package/build/angular/components/pressable/index.js.map +1 -1
- package/build/angular/components/refresh-control/index.d.ts +8 -4
- package/build/angular/components/refresh-control/index.js +52 -16
- package/build/angular/components/refresh-control/index.js.map +1 -1
- package/build/angular/components/safe-area-view/index.d.ts +9 -4
- package/build/angular/components/safe-area-view/index.js +46 -9
- package/build/angular/components/safe-area-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/index.android.d.ts +2 -3
- package/build/angular/components/scroll-view/index.android.js +49 -52
- package/build/angular/components/scroll-view/index.android.js.map +1 -1
- package/build/angular/components/scroll-view/index.ios.d.ts +2 -3
- package/build/angular/components/scroll-view/index.ios.js +15 -17
- package/build/angular/components/scroll-view/index.ios.js.map +1 -1
- package/build/angular/components/scroll-view/projection.d.ts +3 -0
- package/build/angular/components/scroll-view/projection.js +68 -7
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.d.ts +14 -5
- package/build/angular/components/scroll-view/shared.js +198 -69
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.js +14 -19
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/section-list/index.d.ts +2 -1
- package/build/angular/components/section-list/index.js +31 -39
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/switch/index.android.d.ts +7 -3
- package/build/angular/components/switch/index.android.js +38 -10
- package/build/angular/components/switch/index.android.js.map +1 -1
- package/build/angular/components/switch/index.ios.d.ts +7 -3
- package/build/angular/components/switch/index.ios.js +38 -10
- package/build/angular/components/switch/index.ios.js.map +1 -1
- package/build/angular/components/switch/shared.d.ts +12 -3
- package/build/angular/components/switch/shared.js +66 -5
- package/build/angular/components/switch/shared.js.map +1 -1
- package/build/angular/components/text-input.d.ts +10 -2
- package/build/angular/components/text-input.js +46 -6
- package/build/angular/components/text-input.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +17 -8
- package/build/angular/components/touchable/index.js +78 -19
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +6 -3
- package/build/angular/components/touchable-native-feedback/index.js +61 -25
- package/build/angular/components/touchable-native-feedback/index.js.map +1 -1
- package/build/angular/components/virtualized-list/directives.js +18 -18
- package/build/angular/components/virtualized-list/index.d.ts +12 -4
- package/build/angular/components/virtualized-list/index.js +163 -68
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/directives.js +12 -12
- package/build/angular/components/virtualized-section-list/index.d.ts +2 -1
- package/build/angular/components/virtualized-section-list/index.js +6 -4
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +1 -1
- package/build/angular/components.js +5 -0
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.js +29 -35
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +19 -22
- package/build/angular/create-tunnel/index.js.map +1 -1
- package/build/angular/descriptor-to-angular/index.js +3 -3
- package/build/angular/index.d.ts +4 -2
- package/build/angular/index.js +2 -2
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/animated-leaf-binder.d.ts +1 -7
- package/build/angular/modules/animated/animated-leaf-binder.js +22 -72
- package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +11 -6
- package/build/angular/modules/animated/create-animated-component.js +83 -50
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/app-registry/index.js.map +1 -1
- package/build/angular/modules/status-bar/index.js +3 -3
- package/build/angular/primitives/index.d.ts +1 -1
- package/build/angular/primitives/index.js +46 -46
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +39 -1
- package/build/angular/primitives/shared.js +110 -9
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/render/index.js +23 -35
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.js +3 -3
- package/build/angular/services/window-dimensions.service.js +3 -3
- package/package.json +13 -8
- package/src/anchor-host-registry.ts +2 -2
- package/src/components/activity-indicator/index.android.ts +2 -0
- package/src/components/activity-indicator/index.ios.ts +2 -0
- package/src/components/button.ts +28 -14
- package/src/components/flat-list/index.ts +34 -36
- package/src/components/image/index.android.ts +50 -6
- package/src/components/image/index.ios.ts +50 -6
- package/src/components/image/shared.ts +34 -2
- package/src/components/image-background.ts +2 -1
- package/src/components/input-accessory-view/index.ts +42 -4
- package/src/components/keyboard-avoiding-view/index.ts +7 -1
- package/src/components/modal/index.ts +51 -26
- package/src/components/pressable/index.ts +67 -8
- package/src/components/refresh-control/index.ts +54 -12
- package/src/components/safe-area-view/index.ts +50 -5
- package/src/components/scroll-view/index.android.ts +34 -41
- package/src/components/scroll-view/index.ios.ts +10 -21
- package/src/components/scroll-view/projection.ts +79 -7
- package/src/components/scroll-view/shared.ts +223 -68
- package/src/components/scroll-view/sticky-header.ts +11 -16
- package/src/components/section-list/index.ts +27 -36
- package/src/components/switch/index.android.ts +48 -7
- package/src/components/switch/index.ios.ts +48 -7
- package/src/components/switch/shared.ts +79 -3
- package/src/components/text-input.ts +52 -2
- package/src/components/touchable/index.ts +76 -16
- package/src/components/touchable-native-feedback/index.ts +62 -21
- package/src/components/virtualized-list/index.ts +137 -44
- package/src/components/virtualized-section-list/index.ts +2 -1
- package/src/components.ts +5 -0
- package/src/create-portal/index.ts +23 -29
- package/src/create-tunnel/index.ts +13 -16
- package/src/index.ts +30 -1
- package/src/modules/animated/animated-leaf-binder.ts +28 -77
- package/src/modules/animated/create-animated-component.ts +75 -36
- package/src/modules/app-registry/index.ts +6 -8
- package/src/primitives/index.ts +1 -0
- package/src/primitives/shared.ts +102 -3
- package/src/render/index.ts +23 -35
- package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/angular/modules/animated/animated-leaf-binder/index.js +0 -91
- package/build/angular/modules/animated/animated-leaf-binder/index.js.map +0 -1
- package/src/__tests__/responder-change-detection.test.ts +0 -102
- package/src/__tests__/responder-nested-cd.test.ts +0 -113
- package/src/angular-gaps.test.ts +0 -142
- package/src/angular-imports.test.ts +0 -100
- package/src/components/activity-indicator/activity-indicator-class-style.test.ts +0 -44
- package/src/components/activity-indicator/activity-indicator.test.ts +0 -131
- package/src/components/flat-list/column-wrapper-style-class.test.ts +0 -116
- package/src/components/flat-list/flat-list-array-style.test.ts +0 -131
- package/src/components/flat-list/flat-list-class-style.test.ts +0 -74
- package/src/components/flat-list/flat-list-scroll-containment.test.ts +0 -107
- package/src/components/flat-list/flat-list.test.ts +0 -99
- package/src/components/image/image-class-style.test.ts +0 -46
- package/src/components/image-background-class-style.test.ts +0 -48
- package/src/components/image-background-style-class.test.ts +0 -79
- package/src/components/input-accessory-view/input-accessory-view.test.ts +0 -43
- package/src/components/keyboard-avoiding-view/keyboard-avoiding-view.test.ts +0 -54
- package/src/components/modal/modal.test.ts +0 -43
- package/src/components/pressable/pressable.test.ts +0 -85
- package/src/components/refresh-control/refresh-control.test.ts +0 -41
- package/src/components/safe-area-view/safe-area-view.test.ts +0 -43
- package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +0 -123
- package/src/components/scroll-view/content-container-style-class.test.ts +0 -75
- package/src/components/scroll-view/scroll-view-class-style.test.ts +0 -134
- package/src/components/scroll-view/scroll-view-projection.test.ts +0 -194
- package/src/components/switch/android-switch-class.test.ts +0 -42
- package/src/components/switch/switch.test.ts +0 -92
- package/src/components/text-input-class-style.test.ts +0 -54
- package/src/components/touchable/touchable.test.ts +0 -102
- package/src/components/touchable-native-feedback/touchable-native-feedback.test.ts +0 -49
- package/src/components/virtualized-list/virtualized-list-class-style.test.ts +0 -76
- package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +0 -60
- package/src/create-portal/create-portal.test.ts +0 -90
- package/src/create-tunnel/create-tunnel.test.ts +0 -120
- package/src/descriptor-to-angular/descriptor-outlet.test.ts +0 -94
- package/src/host-instance/host-instance.test.ts +0 -49
- package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +0 -116
- package/src/modules/animated/animated-leaf-binder/index.ts +0 -101
- package/src/modules/animated/animated-leaf-binder.test.ts +0 -116
- package/src/modules/animated/animated-scroll-view.test.ts +0 -102
- package/src/modules/app-registry/app-registry.test.ts +0 -106
- package/src/modules/status-bar/status-bar.test.ts +0 -50
- package/src/render/render.test.ts +0 -147
- package/src/renderer/renderer.test.ts +0 -203
- package/src/services/services.test.ts +0 -163
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// Regression test for the anchor/class bug: VirtualizedList is its own
|
|
2
|
-
// ANCHOR_HOST_COMPONENTS entry — a class= on <VirtualizedList> resolves onto VirtualizedList's OWN
|
|
3
|
-
// anchor and needs its OWN anchorHostStyle merge (see index.ts's recomputeView), it is NOT
|
|
4
|
-
// transitively fixed by ScrollView's own fix one level down. Mirrors pressable.test.ts's
|
|
5
|
-
// "resolves a class=" case.
|
|
6
|
-
import '@angular/compiler';
|
|
7
|
-
import { Component } from '@angular/core';
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
10
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
11
|
-
|
|
12
|
-
import { mount, unmount } from '../../render';
|
|
13
|
-
import { VirtualizedList } from './index';
|
|
14
|
-
import { VListItemDirective } from './directives';
|
|
15
|
-
|
|
16
|
-
const ROOT_TAG = 952;
|
|
17
|
-
const fabric = installFabric();
|
|
18
|
-
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
19
|
-
|
|
20
|
-
interface IRow {
|
|
21
|
-
id: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const rows: IRow[] = [{ id: 'a' }, { id: 'b' }];
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'symbiote-virtualized-list-class-host',
|
|
28
|
-
standalone: true,
|
|
29
|
-
imports: [VirtualizedList, VListItemDirective],
|
|
30
|
-
template: `
|
|
31
|
-
<VirtualizedList
|
|
32
|
-
class="card"
|
|
33
|
-
[data]="rows"
|
|
34
|
-
[getItem]="getItem"
|
|
35
|
-
[getItemCount]="getItemCount"
|
|
36
|
-
[getItemLayout]="getItemLayout"
|
|
37
|
-
>
|
|
38
|
-
<ng-template vListItem let-item>
|
|
39
|
-
<symbiote-text [testID]="item.id">{{ item.id }}</symbiote-text>
|
|
40
|
-
</ng-template>
|
|
41
|
-
</VirtualizedList>
|
|
42
|
-
`,
|
|
43
|
-
})
|
|
44
|
-
class VirtualizedListClassHost {
|
|
45
|
-
rows = rows;
|
|
46
|
-
getItem = (data: readonly IRow[], index: number): IRow => data[index];
|
|
47
|
-
getItemCount = (data: readonly IRow[]): number => data.length;
|
|
48
|
-
getItemLayout = (
|
|
49
|
-
_data: unknown,
|
|
50
|
-
index: number,
|
|
51
|
-
): { length: number; offset: number; index: number } => ({
|
|
52
|
-
length: 30,
|
|
53
|
-
offset: 30 * index,
|
|
54
|
-
index,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
beforeEach(() => {
|
|
59
|
-
fabric.reset();
|
|
60
|
-
registerStyles({ card: { backgroundColor: 'red' } });
|
|
61
|
-
});
|
|
62
|
-
afterEach(() => {
|
|
63
|
-
unmount(ROOT_TAG);
|
|
64
|
-
clearGlobalStyles();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('VirtualizedList anchor class= resolution', () => {
|
|
68
|
-
it('resolves a class= on the VirtualizedList use site onto the real committed scroll host', async () => {
|
|
69
|
-
mount(ROOT_TAG, VirtualizedListClassHost);
|
|
70
|
-
await tick();
|
|
71
|
-
await tick();
|
|
72
|
-
|
|
73
|
-
const node = fabric.find(n => n.props.backgroundColor === 'red');
|
|
74
|
-
expect(node, 'a real Fabric node carries the class-derived style').toBeDefined();
|
|
75
|
-
});
|
|
76
|
-
});
|
package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// Regression test for the anchor/class bug: VirtualizedSectionList is its
|
|
2
|
-
// own ANCHOR_HOST_COMPONENTS entry — a class= on <VirtualizedSectionList> resolves onto its OWN
|
|
3
|
-
// anchor and needs its OWN anchorHostStyle merge (see index.ts's resolvedStyle getter), it is NOT
|
|
4
|
-
// transitively fixed by VirtualizedList's/ScrollView's own fixes further down. Mirrors
|
|
5
|
-
// pressable.test.ts's "resolves a class=" case.
|
|
6
|
-
import '@angular/compiler';
|
|
7
|
-
import { Component } from '@angular/core';
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
10
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
11
|
-
import type { ISection } from '@symbiote-native/components';
|
|
12
|
-
|
|
13
|
-
import { mount, unmount } from '../../render';
|
|
14
|
-
import { VirtualizedSectionList, VSectionItemDirective } from './index';
|
|
15
|
-
|
|
16
|
-
const ROOT_TAG = 954;
|
|
17
|
-
const fabric = installFabric();
|
|
18
|
-
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
19
|
-
|
|
20
|
-
interface IRow {
|
|
21
|
-
id: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const sections: ISection<IRow>[] = [{ title: 'A', data: [{ id: 'a1' }, { id: 'a2' }] }];
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'symbiote-virtualized-section-list-class-host',
|
|
28
|
-
standalone: true,
|
|
29
|
-
imports: [VirtualizedSectionList, VSectionItemDirective],
|
|
30
|
-
template: `
|
|
31
|
-
<VirtualizedSectionList class="card" [sections]="sections">
|
|
32
|
-
<ng-template vSectionItem let-item>
|
|
33
|
-
<symbiote-text [testID]="item.id">{{ item.id }}</symbiote-text>
|
|
34
|
-
</ng-template>
|
|
35
|
-
</VirtualizedSectionList>
|
|
36
|
-
`,
|
|
37
|
-
})
|
|
38
|
-
class VirtualizedSectionListClassHost {
|
|
39
|
-
sections = sections;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
fabric.reset();
|
|
44
|
-
registerStyles({ card: { backgroundColor: 'red' } });
|
|
45
|
-
});
|
|
46
|
-
afterEach(() => {
|
|
47
|
-
unmount(ROOT_TAG);
|
|
48
|
-
clearGlobalStyles();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('VirtualizedSectionList anchor class= resolution', () => {
|
|
52
|
-
it('resolves a class= on the VirtualizedSectionList use site onto the real committed scroll host', async () => {
|
|
53
|
-
mount(ROOT_TAG, VirtualizedSectionListClassHost);
|
|
54
|
-
await tick();
|
|
55
|
-
await tick();
|
|
56
|
-
|
|
57
|
-
const node = fabric.find(n => n.props.backgroundColor === 'red');
|
|
58
|
-
expect(node, 'a real Fabric node carries the class-derived style').toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// Proves createPortal (create-portal.ts) paints content into an already-mounted destination
|
|
2
|
-
// WITHIN THE SAME SURFACE — mirrors adapters/react/src/create-portal.test.tsx's "same surface,
|
|
3
|
-
// already-mounted target" case (Angular has no cross-surface equivalent of this primitive;
|
|
4
|
-
// createTunnel covers that, see create-tunnel.test.ts).
|
|
5
|
-
|
|
6
|
-
import '@angular/compiler';
|
|
7
|
-
import { Component, signal } from '@angular/core';
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
10
|
-
|
|
11
|
-
import { mount, unmount } from '../render';
|
|
12
|
-
import { ViewHost, TextHost } from '../primitives';
|
|
13
|
-
import { PortalDirective, PortalOutletDirective } from './index';
|
|
14
|
-
|
|
15
|
-
const ROOT_TAG = 930;
|
|
16
|
-
|
|
17
|
-
const fabric = installFabric();
|
|
18
|
-
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
19
|
-
const settle = async (): Promise<void> => {
|
|
20
|
-
await tick();
|
|
21
|
-
await tick();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
let capturedHost: HostApp | undefined;
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'symbiote-portal-host-app',
|
|
28
|
-
standalone: true,
|
|
29
|
-
imports: [ViewHost, TextHost, PortalDirective, PortalOutletDirective],
|
|
30
|
-
template: `
|
|
31
|
-
<View>
|
|
32
|
-
<View portalOutlet #overlayHost="portalOutlet" testID="overlay-host"></View>
|
|
33
|
-
@if (visible()) {
|
|
34
|
-
<View *portal="overlayHost"><Text>portaled content</Text></View>
|
|
35
|
-
}
|
|
36
|
-
</View>
|
|
37
|
-
`,
|
|
38
|
-
})
|
|
39
|
-
class HostApp {
|
|
40
|
-
readonly visible = signal(false);
|
|
41
|
-
|
|
42
|
-
constructor() {
|
|
43
|
-
// Captures the live component instance so the test can drive its signals after mount.
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
45
|
-
capturedHost = this;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
beforeEach(() => {
|
|
50
|
-
capturedHost = undefined;
|
|
51
|
-
fabric.reset();
|
|
52
|
-
});
|
|
53
|
-
afterEach(() => unmount(ROOT_TAG));
|
|
54
|
-
|
|
55
|
-
function walk(nodes: IFakeNode[], visit: (node: IFakeNode) => void): void {
|
|
56
|
-
for (const node of nodes) {
|
|
57
|
-
visit(node);
|
|
58
|
-
walk(node.children, visit);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function findText(text: string): IFakeNode | undefined {
|
|
63
|
-
let found: IFakeNode | undefined;
|
|
64
|
-
walk(fabric.committed, node => {
|
|
65
|
-
if (node.viewName === 'RCTRawText' && node.props.text === text) found = node;
|
|
66
|
-
});
|
|
67
|
-
return found;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
describe('createPortal (Angular) — same-surface delivery', () => {
|
|
71
|
-
it('renders nothing until the portal is toggled on', async () => {
|
|
72
|
-
mount(ROOT_TAG, HostApp);
|
|
73
|
-
await settle();
|
|
74
|
-
expect(findText('portaled content')).toBeUndefined();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('paints content into the outlet once toggled on, and removes it once toggled off', async () => {
|
|
78
|
-
mount(ROOT_TAG, HostApp);
|
|
79
|
-
await settle();
|
|
80
|
-
|
|
81
|
-
if (!capturedHost) throw new Error('host was not captured');
|
|
82
|
-
capturedHost.visible.set(true);
|
|
83
|
-
await settle();
|
|
84
|
-
expect(findText('portaled content'), 'painted once the portal renders').toBeDefined();
|
|
85
|
-
|
|
86
|
-
capturedHost.visible.set(false);
|
|
87
|
-
await settle();
|
|
88
|
-
expect(findText('portaled content'), 'gone once the portal unmounts').toBeUndefined();
|
|
89
|
-
});
|
|
90
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
// Proves createTunnel (create-tunnel.ts) actually delivers across two GENUINELY separate,
|
|
2
|
-
// independently-mounted SymbioteSurfaces — the same proof React's/Vue's create-tunnel tests
|
|
3
|
-
// give, via two real `mount()` calls on different rootTags. Mirrors
|
|
4
|
-
// adapters/react/src/create-tunnel.test.tsx and adapters/vue/src/create-tunnel.test.ts.
|
|
5
|
-
|
|
6
|
-
import '@angular/compiler';
|
|
7
|
-
import { Component } from '@angular/core';
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
-
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
10
|
-
|
|
11
|
-
import { mount, unmount } from '../render';
|
|
12
|
-
import { ViewHost, TextHost } from '../primitives';
|
|
13
|
-
import { createTunnel, TunnelInDirective, TunnelOut } from './index';
|
|
14
|
-
|
|
15
|
-
const SOURCE_TAG = 920;
|
|
16
|
-
const TARGET_TAG = 921;
|
|
17
|
-
|
|
18
|
-
const fabric = installFabric();
|
|
19
|
-
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
20
|
-
const settle = async (): Promise<void> => {
|
|
21
|
-
await tick();
|
|
22
|
-
await tick();
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
beforeEach(() => fabric.reset());
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
unmount(SOURCE_TAG);
|
|
28
|
-
unmount(TARGET_TAG);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
function walk(nodes: IFakeNode[], visit: (node: IFakeNode) => void): void {
|
|
32
|
-
for (const node of nodes) {
|
|
33
|
-
visit(node);
|
|
34
|
-
walk(node.children, visit);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function findText(text: string): IFakeNode | undefined {
|
|
39
|
-
let found: IFakeNode | undefined;
|
|
40
|
-
walk(fabric.committed, node => {
|
|
41
|
-
if (node.viewName === 'RCTRawText' && node.props.text === text) found = node;
|
|
42
|
-
});
|
|
43
|
-
return found;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
describe('createTunnel (Angular) — genuine cross-surface delivery', () => {
|
|
47
|
-
it('paints content registered by surface A on surface B, a DIFFERENT mounted surface', async () => {
|
|
48
|
-
const tunnel = createTunnel();
|
|
49
|
-
|
|
50
|
-
@Component({
|
|
51
|
-
selector: 'symbiote-tunnel-source-app',
|
|
52
|
-
standalone: true,
|
|
53
|
-
imports: [TextHost, TunnelInDirective],
|
|
54
|
-
template: '<Text *tunnelIn="tunnel">ported across surfaces</Text>',
|
|
55
|
-
})
|
|
56
|
-
class SourceApp {
|
|
57
|
-
readonly tunnel = tunnel;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@Component({
|
|
61
|
-
selector: 'symbiote-tunnel-target-app',
|
|
62
|
-
standalone: true,
|
|
63
|
-
imports: [ViewHost, TunnelOut],
|
|
64
|
-
template: '<View><tunnel-out [tunnel]="tunnel" /></View>',
|
|
65
|
-
})
|
|
66
|
-
class TargetApp {
|
|
67
|
-
readonly tunnel = tunnel;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Surface A registers, fully independently, before surface B ever mounts.
|
|
71
|
-
mount(SOURCE_TAG, SourceApp);
|
|
72
|
-
// Surface B mounts SEPARATELY (its own rootTag, its own SymbioteSurface) and reads the
|
|
73
|
-
// tunnel via its own TunnelOut — no ref, no rootTag lookup, no shared Fabric node at all.
|
|
74
|
-
mount(TARGET_TAG, TargetApp);
|
|
75
|
-
await settle();
|
|
76
|
-
|
|
77
|
-
// fake-fabric's `committed` is last-write-wins across rootTags (core/test-utils
|
|
78
|
-
// limitation, not the engine's), so after mounting B second it reflects B's own tree.
|
|
79
|
-
expect(
|
|
80
|
-
findText('ported across surfaces'),
|
|
81
|
-
'content is present in the LAST-committed tree (surface B)',
|
|
82
|
-
).toBeDefined();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('removes the content from the target once the source unmounts', async () => {
|
|
86
|
-
const tunnel = createTunnel();
|
|
87
|
-
|
|
88
|
-
@Component({
|
|
89
|
-
selector: 'symbiote-tunnel-source-app-2',
|
|
90
|
-
standalone: true,
|
|
91
|
-
imports: [TextHost, TunnelInDirective],
|
|
92
|
-
template: '<Text *tunnelIn="tunnel">still here</Text>',
|
|
93
|
-
})
|
|
94
|
-
class SourceApp {
|
|
95
|
-
readonly tunnel = tunnel;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@Component({
|
|
99
|
-
selector: 'symbiote-tunnel-target-app-2',
|
|
100
|
-
standalone: true,
|
|
101
|
-
imports: [ViewHost, TunnelOut],
|
|
102
|
-
template: '<View><tunnel-out [tunnel]="tunnel" /></View>',
|
|
103
|
-
})
|
|
104
|
-
class TargetApp {
|
|
105
|
-
readonly tunnel = tunnel;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
mount(SOURCE_TAG, SourceApp);
|
|
109
|
-
mount(TARGET_TAG, TargetApp);
|
|
110
|
-
await settle();
|
|
111
|
-
expect(findText('still here'), 'present while the source is mounted').toBeDefined();
|
|
112
|
-
|
|
113
|
-
// Tearing down surface A destroys TunnelInDirective, whose ngOnDestroy unregisters from
|
|
114
|
-
// the shared store; surface B's TunnelOut effect reacts on its own next tick and destroys
|
|
115
|
-
// its view.
|
|
116
|
-
unmount(SOURCE_TAG);
|
|
117
|
-
await settle();
|
|
118
|
-
expect(findText('still here'), 'gone from surface B after the source unmounts').toBeUndefined();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import '@angular/compiler';
|
|
2
|
-
import { Component, signal } from '@angular/core';
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
4
|
-
import { el, txt, type IDescriptor } from '@symbiote-native/components';
|
|
5
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
6
|
-
|
|
7
|
-
import { mount, unmount } from '../render';
|
|
8
|
-
import { DescriptorOutlet } from './index.ts';
|
|
9
|
-
|
|
10
|
-
const ROOT_TAG = 904;
|
|
11
|
-
const fabric = installFabric();
|
|
12
|
-
|
|
13
|
-
let capturedHost: DescriptorOutletHost | undefined;
|
|
14
|
-
|
|
15
|
-
async function flushAngular(): Promise<void> {
|
|
16
|
-
await Promise.resolve();
|
|
17
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function currentOutletChild() {
|
|
21
|
-
const child = fabric.appRoot().children[0];
|
|
22
|
-
if (!child) throw new Error('descriptor outlet rendered no root child');
|
|
23
|
-
return child;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@Component({
|
|
27
|
-
selector: 'symbiote-descriptor-outlet-host',
|
|
28
|
-
standalone: true,
|
|
29
|
-
imports: [DescriptorOutlet],
|
|
30
|
-
template: '<symbiote-descriptor-outlet [node]="node()" />',
|
|
31
|
-
})
|
|
32
|
-
class DescriptorOutletHost {
|
|
33
|
-
readonly node = signal<IDescriptor>(
|
|
34
|
-
el('symbiote-view', { testID: 'root', style: { width: 10 } }, [txt({}, ['hello'])]),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
constructor() {
|
|
38
|
-
// Captures the live component instance so the test can drive its signals after mount.
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
40
|
-
capturedHost = this;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
capturedHost = undefined;
|
|
46
|
-
fabric.reset();
|
|
47
|
-
});
|
|
48
|
-
afterEach(() => unmount(ROOT_TAG));
|
|
49
|
-
|
|
50
|
-
describe('DescriptorOutlet', () => {
|
|
51
|
-
it('renders a Descriptor tree through Renderer2', async () => {
|
|
52
|
-
mount(ROOT_TAG, DescriptorOutletHost);
|
|
53
|
-
await flushAngular();
|
|
54
|
-
|
|
55
|
-
expect(fabric.serialize(fabric.appRoot().children)).toBe(
|
|
56
|
-
'RCTView(RCTText(RCTRawText "hello"))',
|
|
57
|
-
);
|
|
58
|
-
const root = currentOutletChild();
|
|
59
|
-
expect(root.props.testID).toBe('root');
|
|
60
|
-
expect(root.props.width).toBe(10);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('does not recommit a structurally identical descriptor through anchor flattening', async () => {
|
|
64
|
-
mount(ROOT_TAG, DescriptorOutletHost);
|
|
65
|
-
await flushAngular();
|
|
66
|
-
|
|
67
|
-
const completeRootBefore = fabric.counts.completeRoot;
|
|
68
|
-
|
|
69
|
-
capturedHost?.node.set(
|
|
70
|
-
el('symbiote-view', { testID: 'root', style: { width: 10 } }, [txt({}, ['hello'])]),
|
|
71
|
-
);
|
|
72
|
-
await flushAngular();
|
|
73
|
-
|
|
74
|
-
expect(fabric.counts.completeRoot).toBe(completeRootBefore);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('patches same type/key descriptors without recreating the Fabric node', async () => {
|
|
78
|
-
mount(ROOT_TAG, DescriptorOutletHost);
|
|
79
|
-
await flushAngular();
|
|
80
|
-
|
|
81
|
-
const before = currentOutletChild();
|
|
82
|
-
const createdBefore = fabric.counts.createNode;
|
|
83
|
-
|
|
84
|
-
capturedHost?.node.set(
|
|
85
|
-
el('symbiote-view', { testID: 'root', style: { width: 20 } }, [txt({}, ['hello'])]),
|
|
86
|
-
);
|
|
87
|
-
await flushAngular();
|
|
88
|
-
|
|
89
|
-
const after = currentOutletChild();
|
|
90
|
-
expect(fabric.counts.createNode).toBe(createdBefore);
|
|
91
|
-
expect(after.instanceHandle).toBe(before.instanceHandle);
|
|
92
|
-
expect(after.props.width).toBe(20);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Co-located test for findNodeHandle, the Angular twin of adapters/vue/src/host-instance/host-instance.test.ts.
|
|
2
|
-
// Proves the Angular adapter can resolve a raw engine node, an engine public instance, a bare
|
|
3
|
-
// reactTag, and null/undefined to a native tag using the engine's getNativeTag.
|
|
4
|
-
|
|
5
|
-
import { describe, expect, it } from 'vitest';
|
|
6
|
-
import { createElement, createSurface, getNativeTag } from '@symbiote-native/engine';
|
|
7
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
8
|
-
import { findNodeHandle } from './index';
|
|
9
|
-
|
|
10
|
-
installFabric();
|
|
11
|
-
const ROOT_TAG = 708;
|
|
12
|
-
|
|
13
|
-
describe('Angular findNodeHandle on the engine', () => {
|
|
14
|
-
it('returns the committed tag for a raw engine node', async () => {
|
|
15
|
-
const surface = createSurface(ROOT_TAG);
|
|
16
|
-
const node = createElement('RCTView');
|
|
17
|
-
surface.appendChild(node);
|
|
18
|
-
surface.commit();
|
|
19
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
20
|
-
|
|
21
|
-
const tag = getNativeTag(node);
|
|
22
|
-
expect(tag).toBeGreaterThan(0);
|
|
23
|
-
expect(findNodeHandle(node)).toBe(tag);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('returns the committed tag for a public instance (toPublicInstance graft)', async () => {
|
|
27
|
-
const surface = createSurface(ROOT_TAG + 1);
|
|
28
|
-
const node = createElement('RCTView');
|
|
29
|
-
surface.appendChild(node);
|
|
30
|
-
surface.commit();
|
|
31
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
32
|
-
|
|
33
|
-
const tag = getNativeTag(node);
|
|
34
|
-
// The public instance is the same identity as the engine node; findNodeHandle accepts it.
|
|
35
|
-
const publicInstance = node;
|
|
36
|
-
expect(findNodeHandle(publicInstance)).toBe(tag);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('passes a bare number through idempotently', () => {
|
|
40
|
-
expect(findNodeHandle(42)).toBe(42);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('returns null for null, undefined, and unknown inputs', () => {
|
|
44
|
-
expect(findNodeHandle(null)).toBeNull();
|
|
45
|
-
expect(findNodeHandle(undefined)).toBeNull();
|
|
46
|
-
expect(findNodeHandle('not-a-node' as any)).toBeNull();
|
|
47
|
-
expect(findNodeHandle({} as any)).toBeNull();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// Unit tests for AnimatedLeafBinder in isolation — no Angular involved. Both
|
|
2
|
-
// AnimatedComponentBase and AnimatedImage delegate their leaf-lifecycle (build a leaf,
|
|
3
|
-
// bind it to the committed Fabric node, swap leaves on re-render, tear down) to this Pure
|
|
4
|
-
// Fabrication; these tests exercise the binder directly against a real engine node
|
|
5
|
-
// (SymbioteSurface + the fake Fabric backend), so a break here isolates to the shared
|
|
6
|
-
// orchestration rather than either owning component.
|
|
7
|
-
import { beforeEach, describe, expect, it } from 'vitest';
|
|
8
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
9
|
-
import {
|
|
10
|
-
AnimatedValue,
|
|
11
|
-
createElement,
|
|
12
|
-
createSurface,
|
|
13
|
-
type ISymbioteNode,
|
|
14
|
-
} from '@symbiote-native/engine';
|
|
15
|
-
import { AnimatedLeafBinder } from './index';
|
|
16
|
-
|
|
17
|
-
const fabric = installFabric();
|
|
18
|
-
let nextRootTag = 9001;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => fabric.reset());
|
|
21
|
-
|
|
22
|
-
// A node that has already gone through a commit — whenCommitted fires synchronously.
|
|
23
|
-
function committedNode(): ISymbioteNode {
|
|
24
|
-
const surface = createSurface(nextRootTag++);
|
|
25
|
-
const node = createElement('RCTView');
|
|
26
|
-
surface.appendChild(node);
|
|
27
|
-
surface.commit();
|
|
28
|
-
return node;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// A node that is attached to a surface but has NOT been committed yet — whenCommitted
|
|
32
|
-
// must defer until the caller commits the surface.
|
|
33
|
-
function uncommittedNode(): { node: ISymbioteNode; commit: () => void } {
|
|
34
|
-
const surface = createSurface(nextRootTag++);
|
|
35
|
-
const node = createElement('RCTView');
|
|
36
|
-
surface.appendChild(node);
|
|
37
|
-
return { node, commit: () => surface.commit() };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// `appRoot()` unwraps RN's synthetic box-none AppContainer, which is ALSO an RCTView, so a
|
|
41
|
-
// plain `fabric.find(viewName === 'RCTView')` would match it instead of the node under test.
|
|
42
|
-
// Each test appends exactly one child, so the container's first (only) child is the real node.
|
|
43
|
-
function fakeView(): ReturnType<typeof fabric.find> {
|
|
44
|
-
return fabric.appRoot().children[0];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
describe('AnimatedLeafBinder', () => {
|
|
48
|
-
it('creates and attaches an AnimatedProps leaf, wiring value changes onto the host node', () => {
|
|
49
|
-
const node = committedNode();
|
|
50
|
-
const binder = new AnimatedLeafBinder(() => node, 'test');
|
|
51
|
-
const opacity = new AnimatedValue(1);
|
|
52
|
-
|
|
53
|
-
// The leaf itself never pushes the INITIAL value — that first paint comes from the
|
|
54
|
-
// owning component's own reduceProps template binding (out of scope here). The leaf
|
|
55
|
-
// only drives value CHANGES after it is bound.
|
|
56
|
-
binder.reconcile({ style: { opacity } }, false);
|
|
57
|
-
|
|
58
|
-
opacity.setValue(0.4);
|
|
59
|
-
expect(fakeView()?.props.opacity).toBe(0.4);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('binds to the host node via whenCommitted, deferring until the node is actually committed', () => {
|
|
63
|
-
const { node, commit } = uncommittedNode();
|
|
64
|
-
const binder = new AnimatedLeafBinder(() => node, 'test');
|
|
65
|
-
const opacity = new AnimatedValue(1);
|
|
66
|
-
|
|
67
|
-
binder.reconcile({ style: { opacity } }, false);
|
|
68
|
-
// Not committed yet: the value graph exists, but no Fabric node to flush onto.
|
|
69
|
-
opacity.setValue(0.5);
|
|
70
|
-
expect(fabric.created).toHaveLength(0);
|
|
71
|
-
|
|
72
|
-
commit();
|
|
73
|
-
// whenCommitted's post-commit retry binds the leaf now; a subsequent change flushes.
|
|
74
|
-
opacity.setValue(0.7);
|
|
75
|
-
expect(fakeView()?.props.opacity).toBe(0.7);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('attaches the new leaf before detaching the old one on a second reconcile', () => {
|
|
79
|
-
const node = committedNode();
|
|
80
|
-
const binder = new AnimatedLeafBinder(() => node, 'test');
|
|
81
|
-
const shared = new AnimatedValue(1);
|
|
82
|
-
const order: string[] = [];
|
|
83
|
-
const originalAddChild = shared.__addChild.bind(shared);
|
|
84
|
-
const originalRemoveChild = shared.__removeChild.bind(shared);
|
|
85
|
-
shared.__addChild = child => {
|
|
86
|
-
order.push('add');
|
|
87
|
-
originalAddChild(child);
|
|
88
|
-
};
|
|
89
|
-
shared.__removeChild = child => {
|
|
90
|
-
order.push('remove');
|
|
91
|
-
originalRemoveChild(child);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
binder.reconcile({ style: { opacity: shared } }, false);
|
|
95
|
-
order.length = 0; // only care about the SECOND reconcile's swap order
|
|
96
|
-
|
|
97
|
-
binder.reconcile({ style: { opacity: shared } }, false);
|
|
98
|
-
|
|
99
|
-
expect(order).toEqual(['add', 'remove']);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('detaches the leaf and cancels any pending bind on destroy', () => {
|
|
103
|
-
const { node, commit } = uncommittedNode();
|
|
104
|
-
const binder = new AnimatedLeafBinder(() => node, 'test');
|
|
105
|
-
const opacity = new AnimatedValue(1);
|
|
106
|
-
|
|
107
|
-
binder.reconcile({ style: { opacity } }, false);
|
|
108
|
-
binder.destroy();
|
|
109
|
-
commit();
|
|
110
|
-
|
|
111
|
-
// The pending whenCommitted bind was cancelled by destroy, so committing afterward
|
|
112
|
-
// must not retroactively bind the (already-detached) leaf.
|
|
113
|
-
opacity.setValue(0.9);
|
|
114
|
-
expect(fakeView()?.props.opacity).toBeUndefined();
|
|
115
|
-
});
|
|
116
|
-
});
|