@symbiote-native/angular 0.6.0 → 0.6.2
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/babel-register-composed.cjs +11 -3
- package/build/angular/anchor-host-registry.d.ts +2 -0
- package/build/angular/anchor-host-registry.js +84 -0
- package/build/angular/anchor-host-registry.js.map +1 -0
- package/build/angular/components/input-accessory-view/index.d.ts +2 -1
- package/build/angular/components/input-accessory-view/index.js +28 -39
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/projection.js +70 -48
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.js +28 -25
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +6 -7
- package/build/angular/components/scroll-view/sticky-header.js +82 -65
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +2 -5
- package/build/angular/components/touchable/index.js +36 -56
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +5 -20
- package/build/angular/components/virtualized-list/index.js +145 -275
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.js +2 -4
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/index.d.ts +2 -1
- package/build/angular/index.js +6 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +0 -1
- package/build/angular/renderer/index.js +5 -60
- package/build/angular/renderer/index.js.map +1 -1
- package/package.json +8 -4
- package/src/anchor-host-registry.ts +88 -0
- package/src/components/input-accessory-view/index.ts +30 -39
- package/src/components/scroll-view/projection.ts +76 -46
- package/src/components/scroll-view/shared.ts +27 -19
- package/src/components/scroll-view/sticky-header.ts +89 -64
- package/src/components/touchable/index.ts +52 -66
- package/src/components/virtualized-list/index.ts +157 -368
- package/src/components/virtualized-section-list/index.ts +6 -3
- package/src/index.ts +6 -1
- package/src/renderer/index.ts +5 -63
- package/build/bootstrap.d.ts +0 -7
- package/build/bootstrap.js +0 -14
- package/build/components/activity-indicator/index.android.d.ts +0 -9
- package/build/components/activity-indicator/index.android.js +0 -64
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -5
- package/build/components/activity-indicator/index.ios.js +0 -64
- package/build/components/activity-indicator/index.js +0 -3
- package/build/components/activity-indicator/shared.d.ts +0 -64
- package/build/components/activity-indicator/shared.js +0 -424
- package/build/components/button.d.ts +0 -60
- package/build/components/button.js +0 -456
- package/build/components/flat-list/index.d.ts +0 -119
- package/build/components/flat-list/index.js +0 -691
- package/build/components/image/index.android.d.ts +0 -7
- package/build/components/image/index.android.js +0 -88
- package/build/components/image/index.d.ts +0 -1
- package/build/components/image/index.ios.d.ts +0 -7
- package/build/components/image/index.ios.js +0 -88
- package/build/components/image/index.js +0 -2
- package/build/components/image/shared.d.ts +0 -101
- package/build/components/image/shared.js +0 -405
- package/build/components/image-background.d.ts +0 -78
- package/build/components/image-background.js +0 -583
- package/build/components/input-accessory-view/index.d.ts +0 -56
- package/build/components/input-accessory-view/index.js +0 -403
- package/build/components/keyboard-avoiding-view/index.d.ts +0 -79
- package/build/components/keyboard-avoiding-view/index.js +0 -504
- package/build/components/modal/index.d.ts +0 -89
- package/build/components/modal/index.js +0 -529
- package/build/components/pressable/index.d.ts +0 -120
- package/build/components/pressable/index.js +0 -639
- package/build/components/refresh-control/index.d.ts +0 -83
- package/build/components/refresh-control/index.js +0 -509
- package/build/components/safe-area-view/index.d.ts +0 -54
- package/build/components/safe-area-view/index.js +0 -388
- package/build/components/scroll-view/index.android.d.ts +0 -16
- package/build/components/scroll-view/index.android.js +0 -228
- package/build/components/scroll-view/index.d.ts +0 -3
- package/build/components/scroll-view/index.ios.d.ts +0 -6
- package/build/components/scroll-view/index.ios.js +0 -113
- package/build/components/scroll-view/index.js +0 -5
- package/build/components/scroll-view/projection.d.ts +0 -32
- package/build/components/scroll-view/projection.js +0 -311
- package/build/components/scroll-view/shared.d.ts +0 -225
- package/build/components/scroll-view/shared.js +0 -757
- package/build/components/scroll-view/sticky-header.d.ts +0 -38
- package/build/components/scroll-view/sticky-header.js +0 -257
- package/build/components/section-list/index.d.ts +0 -113
- package/build/components/section-list/index.js +0 -768
- package/build/components/switch/index.android.d.ts +0 -17
- package/build/components/switch/index.android.js +0 -107
- package/build/components/switch/index.d.ts +0 -1
- package/build/components/switch/index.ios.d.ts +0 -16
- package/build/components/switch/index.ios.js +0 -106
- package/build/components/switch/index.js +0 -3
- package/build/components/switch/shared.d.ts +0 -37
- package/build/components/switch/shared.js +0 -176
- package/build/components/text-input.d.ts +0 -150
- package/build/components/text-input.js +0 -821
- package/build/components/touchable/index.d.ts +0 -241
- package/build/components/touchable/index.js +0 -1672
- package/build/components/touchable-native-feedback/index.d.ts +0 -80
- package/build/components/touchable-native-feedback/index.js +0 -545
- package/build/components/virtualized-list/directives.d.ts +0 -41
- package/build/components/virtualized-list/directives.js +0 -249
- package/build/components/virtualized-list/index.d.ts +0 -265
- package/build/components/virtualized-list/index.js +0 -1343
- package/build/components/virtualized-section-list/directives.d.ts +0 -28
- package/build/components/virtualized-section-list/directives.js +0 -148
- package/build/components/virtualized-section-list/index.d.ts +0 -175
- package/build/components/virtualized-section-list/index.js +0 -854
- package/build/components.d.ts +0 -39
- package/build/components.js +0 -37
- package/build/create-portal/index.d.ts +0 -17
- package/build/create-portal/index.js +0 -128
- package/build/create-tunnel/index.d.ts +0 -29
- package/build/create-tunnel/index.js +0 -181
- package/build/descriptor-to-angular/index.d.ts +0 -19
- package/build/descriptor-to-angular/index.js +0 -186
- package/build/host-instance/index.d.ts +0 -2
- package/build/host-instance/index.js +0 -36
- package/build/index.d.ts +0 -21
- package/build/index.js +0 -40
- package/build/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/modules/animated/animated-leaf-binder/index.js +0 -90
- package/build/modules/animated/animated-leaf-binder.d.ts +0 -14
- package/build/modules/animated/animated-leaf-binder.js +0 -90
- package/build/modules/animated/create-animated-component.d.ts +0 -48
- package/build/modules/animated/create-animated-component.js +0 -425
- package/build/modules/animated/index.d.ts +0 -51
- package/build/modules/animated/index.js +0 -58
- package/build/modules/app-registry/index.d.ts +0 -7
- package/build/modules/app-registry/index.js +0 -22
- package/build/modules/status-bar/index.d.ts +0 -17
- package/build/modules/status-bar/index.js +0 -121
- package/build/primitives/index.d.ts +0 -32
- package/build/primitives/index.js +0 -386
- package/build/primitives/shared.d.ts +0 -83
- package/build/primitives/shared.js +0 -230
- package/build/render/index.d.ts +0 -11
- package/build/render/index.js +0 -170
- package/build/renderer/index.d.ts +0 -38
- package/build/renderer/index.js +0 -355
- package/build/services/color-scheme.service.d.ts +0 -13
- package/build/services/color-scheme.service.js +0 -72
- package/build/services/index.d.ts +0 -2
- package/build/services/index.js +0 -2
- package/build/services/window-dimensions.service.d.ts +0 -13
- package/build/services/window-dimensions.service.js +0 -83
- 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.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 -202
- package/src/services/services.test.ts +0 -163
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// The device-faithful shape: a ROOT component whose template embeds a CHILD component that
|
|
2
|
-
// owns the responder state + the {{status}} binding (App -> ResponderDemo). Angular 20 compiles
|
|
3
|
-
// components as SignalView (not CheckAlways), so a plain (non-signal) state mutation inside a
|
|
4
|
-
// flat-bag responder callback does NOT dirty the child's reactive consumer, and the scheduler's
|
|
5
|
-
// root detectChanges() will NOT descend into it — the child's {{status}} used to stay stale
|
|
6
|
-
// ("pan does nothing"). SymbioteHostPropsDirective now refreshes its own host component's view
|
|
7
|
-
// right after the callback runs, so the nested child repaints.
|
|
8
|
-
|
|
9
|
-
import '@angular/compiler';
|
|
10
|
-
import { Component } from '@angular/core';
|
|
11
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
12
|
-
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
13
|
-
|
|
14
|
-
import { mount, unmount } from '../render';
|
|
15
|
-
import { ViewHost as View, TextHost as Text, SymbioteHostPropsDirective } from '../primitives';
|
|
16
|
-
import { registerComposedComponent } from '../renderer';
|
|
17
|
-
|
|
18
|
-
const ROOT_TAG = 972;
|
|
19
|
-
const TOUCH_START = 'topTouchStart';
|
|
20
|
-
const TOUCH_MOVE = 'topTouchMove';
|
|
21
|
-
const TOUCH_END = 'topTouchEnd';
|
|
22
|
-
|
|
23
|
-
registerComposedComponent('nested-responder-inner');
|
|
24
|
-
|
|
25
|
-
const fabric = installFabric();
|
|
26
|
-
|
|
27
|
-
function findCommitted(predicate: (node: IFakeNode) => boolean): IFakeNode | undefined {
|
|
28
|
-
const stack = [...fabric.committed];
|
|
29
|
-
while (stack.length > 0) {
|
|
30
|
-
const node = stack.pop();
|
|
31
|
-
if (node === undefined) continue;
|
|
32
|
-
if (predicate(node)) return node;
|
|
33
|
-
stack.push(...node.children);
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function statusText(testID: string): string | undefined {
|
|
39
|
-
const node = findCommitted(n => n.props.testID === testID);
|
|
40
|
-
const raw = node?.children[0];
|
|
41
|
-
return typeof raw?.props.text === 'string' ? raw.props.text : undefined;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function handleFor(testID: string): unknown {
|
|
45
|
-
const node = fabric.find(n => n.props.testID === testID);
|
|
46
|
-
if (!node) throw new Error(`no node created with testID=${testID}`);
|
|
47
|
-
return node.instanceHandle;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const flush = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
51
|
-
|
|
52
|
-
@Component({
|
|
53
|
-
selector: 'nested-responder-inner',
|
|
54
|
-
standalone: true,
|
|
55
|
-
imports: [View, Text, SymbioteHostPropsDirective],
|
|
56
|
-
template: `
|
|
57
|
-
<View [symbioteHostProps]="handlers"></View>
|
|
58
|
-
<Text [symbioteHostProps]="statusProps">{{ status }}</Text>
|
|
59
|
-
`,
|
|
60
|
-
})
|
|
61
|
-
class NestedResponderInner {
|
|
62
|
-
status = 'idle';
|
|
63
|
-
statusProps = { testID: 'nested-status' };
|
|
64
|
-
handlers = {
|
|
65
|
-
testID: 'nested-chip',
|
|
66
|
-
onStartShouldSetResponder: () => true,
|
|
67
|
-
onResponderGrant: () => {
|
|
68
|
-
this.status = 'granted';
|
|
69
|
-
},
|
|
70
|
-
onResponderMove: () => {
|
|
71
|
-
this.status = 'moving';
|
|
72
|
-
},
|
|
73
|
-
onResponderRelease: () => {
|
|
74
|
-
this.status = 'released';
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@Component({
|
|
80
|
-
selector: 'nested-responder-outer',
|
|
81
|
-
standalone: true,
|
|
82
|
-
imports: [View, NestedResponderInner],
|
|
83
|
-
template: `
|
|
84
|
-
<View>
|
|
85
|
-
<nested-responder-inner></nested-responder-inner>
|
|
86
|
-
</View>
|
|
87
|
-
`,
|
|
88
|
-
})
|
|
89
|
-
class NestedResponderOuter {}
|
|
90
|
-
|
|
91
|
-
beforeEach(() => fabric.reset());
|
|
92
|
-
afterEach(() => unmount(ROOT_TAG));
|
|
93
|
-
|
|
94
|
-
describe('Angular nested-child responder CD', () => {
|
|
95
|
-
it('re-renders a nested child component`s state mutated in a flat-bag responder callback', async () => {
|
|
96
|
-
mount(ROOT_TAG, NestedResponderOuter);
|
|
97
|
-
await flush();
|
|
98
|
-
expect(statusText('nested-status')).toBe('idle');
|
|
99
|
-
|
|
100
|
-
const chip = handleFor('nested-chip');
|
|
101
|
-
fabric.fireEvent(chip, TOUCH_START);
|
|
102
|
-
await flush();
|
|
103
|
-
expect(statusText('nested-status')).toBe('granted');
|
|
104
|
-
|
|
105
|
-
fabric.fireEvent(chip, TOUCH_MOVE);
|
|
106
|
-
await flush();
|
|
107
|
-
expect(statusText('nested-status')).toBe('moving');
|
|
108
|
-
|
|
109
|
-
fabric.fireEvent(chip, TOUCH_END);
|
|
110
|
-
await flush();
|
|
111
|
-
expect(statusText('nested-status')).toBe('released');
|
|
112
|
-
});
|
|
113
|
-
});
|
package/src/angular-gaps.test.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
|
|
6
|
-
const readSource = (path: string): string => readFileSync(path, 'utf8');
|
|
7
|
-
|
|
8
|
-
describe('Angular adapter gap regressions', () => {
|
|
9
|
-
it('VirtualizedList exposes accessibility and aria inputs and forwards them to ScrollView', () => {
|
|
10
|
-
const source = readSource('adapters/angular/src/components/virtualized-list/index.ts');
|
|
11
|
-
|
|
12
|
-
expect(source).toContain('@Input() accessibilityLabel?: string');
|
|
13
|
-
expect(source).toContain('@Input() ariaBusy?: boolean');
|
|
14
|
-
expect(source).toContain('[accessibilityLabel]="foldedAccessibility.accessibilityLabel"');
|
|
15
|
-
expect(source).toContain('[accessibilityState]="foldedAccessibility.accessibilityState"');
|
|
16
|
-
expect(source).toContain('[accessibilityRole]="foldedAccessibility.accessibilityRole"');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('Animated namespace exposes AOT-safe built-in entries and documents runtime HOC as a non-goal', () => {
|
|
20
|
-
const animatedSource = readSource('adapters/angular/src/modules/animated/index.ts');
|
|
21
|
-
const componentSource = readSource(
|
|
22
|
-
'adapters/angular/src/modules/animated/create-animated-component.ts',
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
expect(componentSource).toContain('export const AnimatedFlatList');
|
|
26
|
-
expect(componentSource).toContain('export const AnimatedSectionList');
|
|
27
|
-
expect(componentSource).toContain('if (base === View) return AnimatedView');
|
|
28
|
-
expect(componentSource).toContain('if (base === Text) return AnimatedText');
|
|
29
|
-
expect(componentSource).toContain('if (base === Image) return AnimatedImage');
|
|
30
|
-
expect(componentSource).toContain('if (base === ScrollView) return AnimatedScrollView');
|
|
31
|
-
expect(componentSource).toContain('Angular cannot synthesize a component at runtime');
|
|
32
|
-
expect(componentSource).toContain('no JIT under AOT/Metro');
|
|
33
|
-
expect(componentSource).toContain(
|
|
34
|
-
'Author an explicit standalone @Component extending AnimatedComponentBase',
|
|
35
|
-
);
|
|
36
|
-
expect(animatedSource).toContain('View: AnimatedView');
|
|
37
|
-
expect(animatedSource).toContain('Text: AnimatedText');
|
|
38
|
-
expect(animatedSource).toContain('Image: AnimatedImage');
|
|
39
|
-
expect(animatedSource).toContain('ScrollView: AnimatedScrollView');
|
|
40
|
-
expect(animatedSource).toContain('FlatList: AnimatedFlatList');
|
|
41
|
-
expect(animatedSource).toContain('SectionList: AnimatedSectionList');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('AnimatedImage uses composed Image props and normalized load events', () => {
|
|
45
|
-
const source = readSource('adapters/angular/src/modules/animated/create-animated-component.ts');
|
|
46
|
-
|
|
47
|
-
expect(source).toContain('export class AnimatedImage extends ImageBase');
|
|
48
|
-
expect(source).toContain('inputs: ANIMATED_IMAGE_INPUTS');
|
|
49
|
-
expect(source).toContain('outputs: IMAGE_OUTPUTS');
|
|
50
|
-
expect(source).toContain('const resolved = resolveImageProps(reduced)');
|
|
51
|
-
expect(source).toContain('(load)="handleLoad($event)"');
|
|
52
|
-
expect(source).toContain('(error)="handleError($event)"');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('Image prop resolution is shared with AnimatedImage', () => {
|
|
56
|
-
const imageSource = readSource('adapters/angular/src/components/image/shared.ts');
|
|
57
|
-
|
|
58
|
-
expect(imageSource).toContain('export function resolveImageProps');
|
|
59
|
-
expect(imageSource).toContain('get imageProps(): Record<string, unknown>');
|
|
60
|
-
expect(imageSource).toContain('return resolveImageProps(this.imageInputProps)');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('Button forwards its full accessibility and TV-focus surface through TouchableOpacity', () => {
|
|
64
|
-
const buttonSource = readSource('adapters/angular/src/components/button.ts');
|
|
65
|
-
const touchableSource = readSource('adapters/angular/src/components/touchable/index.ts');
|
|
66
|
-
const pressableSource = readSource('adapters/angular/src/components/pressable/index.ts');
|
|
67
|
-
|
|
68
|
-
for (const binding of [
|
|
69
|
-
'[accessible]="true"',
|
|
70
|
-
'[accessibilityLabelledBy]="accessibilityLabelledBy"',
|
|
71
|
-
'[importantForAccessibility]="importantForAccessibility"',
|
|
72
|
-
'[accessibilityLiveRegion]="accessibilityLiveRegion"',
|
|
73
|
-
'[screenReaderFocusable]="screenReaderFocusable"',
|
|
74
|
-
'[accessibilityViewIsModal]="accessibilityViewIsModal"',
|
|
75
|
-
'[accessibilityElementsHidden]="accessibilityElementsHidden"',
|
|
76
|
-
'[accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"',
|
|
77
|
-
'[accessibilityLanguage]="accessibilityLanguage"',
|
|
78
|
-
'[accessibilityRespondsToUserInteraction]="accessibilityRespondsToUserInteraction"',
|
|
79
|
-
'[accessibilityShowsLargeContentViewer]="accessibilityShowsLargeContentViewer"',
|
|
80
|
-
'[accessibilityLargeContentTitle]="accessibilityLargeContentTitle"',
|
|
81
|
-
'(accessibilityAction)="accessibilityAction.emit($event)"',
|
|
82
|
-
'(accessibilityTap)="accessibilityTap.emit($event)"',
|
|
83
|
-
'(magicTap)="magicTap.emit($event)"',
|
|
84
|
-
'(accessibilityEscape)="accessibilityEscape.emit($event)"',
|
|
85
|
-
'[ariaModal]="ariaModal"',
|
|
86
|
-
'[ariaValueMax]="ariaValueMax"',
|
|
87
|
-
'[ariaValueMin]="ariaValueMin"',
|
|
88
|
-
'[ariaValueNow]="ariaValueNow"',
|
|
89
|
-
'[ariaValueText]="ariaValueText"',
|
|
90
|
-
'[hasTVPreferredFocus]="hasTVPreferredFocus"',
|
|
91
|
-
'[nextFocusDown]="nextFocusDown"',
|
|
92
|
-
'[nextFocusForward]="nextFocusForward"',
|
|
93
|
-
'[nextFocusLeft]="nextFocusLeft"',
|
|
94
|
-
'[nextFocusRight]="nextFocusRight"',
|
|
95
|
-
'[nextFocusUp]="nextFocusUp"',
|
|
96
|
-
]) {
|
|
97
|
-
expect(buttonSource).toContain(binding);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
for (const binding of [
|
|
101
|
-
'[accessible]="accessible"',
|
|
102
|
-
'[accessibilityLabelledBy]="accessibilityLabelledBy"',
|
|
103
|
-
'[importantForAccessibility]="importantForAccessibility"',
|
|
104
|
-
'[accessibilityLiveRegion]="accessibilityLiveRegion"',
|
|
105
|
-
'[screenReaderFocusable]="screenReaderFocusable"',
|
|
106
|
-
'[accessibilityViewIsModal]="accessibilityViewIsModal"',
|
|
107
|
-
'[accessibilityElementsHidden]="accessibilityElementsHidden"',
|
|
108
|
-
'[accessibilityIgnoresInvertColors]="accessibilityIgnoresInvertColors"',
|
|
109
|
-
'[accessibilityLanguage]="accessibilityLanguage"',
|
|
110
|
-
'[accessibilityRespondsToUserInteraction]="accessibilityRespondsToUserInteraction"',
|
|
111
|
-
'[accessibilityShowsLargeContentViewer]="accessibilityShowsLargeContentViewer"',
|
|
112
|
-
'[accessibilityLargeContentTitle]="accessibilityLargeContentTitle"',
|
|
113
|
-
'(accessibilityAction)="accessibilityAction.emit($event)"',
|
|
114
|
-
'(accessibilityTap)="accessibilityTap.emit($event)"',
|
|
115
|
-
'(magicTap)="magicTap.emit($event)"',
|
|
116
|
-
'(accessibilityEscape)="accessibilityEscape.emit($event)"',
|
|
117
|
-
'[ariaModal]="ariaModal"',
|
|
118
|
-
'[ariaValueMax]="ariaValueMax"',
|
|
119
|
-
'[ariaValueMin]="ariaValueMin"',
|
|
120
|
-
'[ariaValueNow]="ariaValueNow"',
|
|
121
|
-
'[ariaValueText]="ariaValueText"',
|
|
122
|
-
'[hasTVPreferredFocus]="hasTVPreferredFocus"',
|
|
123
|
-
'[nextFocusDown]="nextFocusDown"',
|
|
124
|
-
'[nextFocusForward]="nextFocusForward"',
|
|
125
|
-
'[nextFocusLeft]="nextFocusLeft"',
|
|
126
|
-
'[nextFocusRight]="nextFocusRight"',
|
|
127
|
-
'[nextFocusUp]="nextFocusUp"',
|
|
128
|
-
]) {
|
|
129
|
-
expect(touchableSource).toContain(binding);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Pressable forwards its resolved props through the shared SymbioteHostPropsDirective
|
|
133
|
-
// (adapters/angular/src/primitives/shared.ts) rather than one `[prop]="x"` binding per
|
|
134
|
-
// key, so the contract to check is: the binding exists, and the `hostProps` getter it
|
|
135
|
-
// reads from actually assembles `accessible` / the folded accessibility bag / TV-focus.
|
|
136
|
-
expect(pressableSource).toContain('[symbioteHostProps]="hostProps"');
|
|
137
|
-
expect(pressableSource).toContain('accessible: this.accessible');
|
|
138
|
-
expect(pressableSource).toContain('...this.foldedAccessibility');
|
|
139
|
-
expect(pressableSource).toContain('hasTVPreferredFocus: this.hasTVPreferredFocus');
|
|
140
|
-
expect(pressableSource).toContain('(accessibilityAction)="emit(accessibilityAction, $event)"');
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// Angular source-import harness regression. Vitest imports adapter source (`src/*.ts`) directly,
|
|
2
|
-
// not the partial-Ivy `ngc` output Metro consumes. The adapter tsconfig must therefore enable
|
|
3
|
-
// TypeScript's legacy decorator lowering so Node never sees raw `@Component` / `@Directive`
|
|
4
|
-
// syntax. Importing `@angular/compiler` enables JIT metadata creation for these source-only tests;
|
|
5
|
-
// production still proves AOT/partial-Ivy through `pnpm --filter @symbiote-native/angular ng:build`.
|
|
6
|
-
|
|
7
|
-
import '@angular/compiler';
|
|
8
|
-
import { ElementRef, Injector, runInInjectionContext } from '@angular/core';
|
|
9
|
-
import { describe, expect, it } from 'vitest';
|
|
10
|
-
import { Image, ScrollView, Text, View } from './components';
|
|
11
|
-
import { VirtualizedList, VListItemDirective } from './components/virtualized-list';
|
|
12
|
-
import { SymbioteHostPropsDirective } from './primitives';
|
|
13
|
-
import {
|
|
14
|
-
Animated,
|
|
15
|
-
AnimatedImage,
|
|
16
|
-
AnimatedScrollView,
|
|
17
|
-
AnimatedText,
|
|
18
|
-
AnimatedView,
|
|
19
|
-
createAnimatedComponent,
|
|
20
|
-
} from './modules/animated';
|
|
21
|
-
|
|
22
|
-
interface IAngularCompiledComponent {
|
|
23
|
-
ɵcmp?: { selectors?: unknown };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface IAngularCompiledDirective {
|
|
27
|
-
ɵdir?: { selectors?: unknown };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
describe('Angular source imports under Vitest', () => {
|
|
31
|
-
it('imports decorated adapter components and directives directly from src', () => {
|
|
32
|
-
expect(VirtualizedList).toBeTypeOf('function');
|
|
33
|
-
expect(VListItemDirective).toBeTypeOf('function');
|
|
34
|
-
expect(AnimatedView).toBeTypeOf('function');
|
|
35
|
-
expect(SymbioteHostPropsDirective).toBeTypeOf('function');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('keeps Angular decorator metadata available to the JIT test runtime', () => {
|
|
39
|
-
expect((VirtualizedList as IAngularCompiledComponent).ɵcmp?.selectors).toEqual([
|
|
40
|
-
['VirtualizedList'],
|
|
41
|
-
]);
|
|
42
|
-
expect((AnimatedView as IAngularCompiledComponent).ɵcmp?.selectors).toEqual([
|
|
43
|
-
['AnimatedView'],
|
|
44
|
-
['symbiote-animated-view'],
|
|
45
|
-
]);
|
|
46
|
-
expect((VListItemDirective as IAngularCompiledDirective).ɵdir?.selectors).toEqual([
|
|
47
|
-
['', 'vListItem', ''],
|
|
48
|
-
]);
|
|
49
|
-
expect((SymbioteHostPropsDirective as IAngularCompiledDirective).ɵdir?.selectors).toEqual([
|
|
50
|
-
['', 'symbioteHostProps', ''],
|
|
51
|
-
]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('imports the Animated namespace without losing component identity', () => {
|
|
55
|
-
expect(Animated.View).toBe(AnimatedView);
|
|
56
|
-
expect(Animated.Text).toBe(AnimatedText);
|
|
57
|
-
expect(Animated.Image).toBe(AnimatedImage);
|
|
58
|
-
expect(Animated.ScrollView).toBe(AnimatedScrollView);
|
|
59
|
-
expect(Animated.FlatList).toBeTypeOf('function');
|
|
60
|
-
expect(Animated.SectionList).toBeTypeOf('function');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('keeps createAnimatedComponent limited to pre-authored AOT-safe Angular wrappers', () => {
|
|
64
|
-
class CustomComponent {}
|
|
65
|
-
|
|
66
|
-
expect(createAnimatedComponent(View)).toBe(AnimatedView);
|
|
67
|
-
expect(createAnimatedComponent(Text)).toBe(AnimatedText);
|
|
68
|
-
expect(createAnimatedComponent(Image)).toBe(AnimatedImage);
|
|
69
|
-
expect(createAnimatedComponent(ScrollView)).toBe(AnimatedScrollView);
|
|
70
|
-
expect(() => createAnimatedComponent(CustomComponent)).toThrow(
|
|
71
|
-
/Angular cannot synthesize a component at runtime \(no JIT compiler under AOT\/Metro\)/,
|
|
72
|
-
);
|
|
73
|
-
expect(() => createAnimatedComponent(CustomComponent)).toThrow(
|
|
74
|
-
/Author an explicit standalone @Component extending AnimatedComponentBase instead/,
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('resolves AnimatedImage props through the composed Image path', () => {
|
|
79
|
-
// AnimatedImage is an ANCHOR_HOST_COMPONENT: its field initializer injects its own
|
|
80
|
-
// ElementRef (anchorHostStyle merge, see create-animated-component.ts), so constructing it
|
|
81
|
-
// outside Angular's component machinery needs an explicit injection context.
|
|
82
|
-
const injector = Injector.create({
|
|
83
|
-
providers: [{ provide: ElementRef, useValue: new ElementRef({}) }],
|
|
84
|
-
});
|
|
85
|
-
const image = runInInjectionContext(injector, () => new AnimatedImage());
|
|
86
|
-
image.src = 'https://example.invalid/image.png';
|
|
87
|
-
image.width = 32;
|
|
88
|
-
image.height = 24;
|
|
89
|
-
image.alt = 'Preview';
|
|
90
|
-
image.animatedProps = { style: { opacity: 0.5 }, testID: 'animated-image' };
|
|
91
|
-
|
|
92
|
-
expect(image.animatedImageProps).toMatchObject({
|
|
93
|
-
testID: 'animated-image',
|
|
94
|
-
accessible: true,
|
|
95
|
-
accessibilityLabel: 'Preview',
|
|
96
|
-
source: [{ uri: 'https://example.invalid/image.png' }],
|
|
97
|
-
style: [undefined, [{ width: 32, height: 24 }, { opacity: 0.5 }]],
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Regression test for the anchor/class bug: ActivityIndicator is its own
|
|
2
|
-
// ANCHOR_HOST_COMPONENTS entry AND renders through DescriptorOutlet (`symbiote-descriptor-outlet`,
|
|
3
|
-
// itself also an anchor entry) — a class= on <ActivityIndicator> resolves onto ActivityIndicator's
|
|
4
|
-
// OWN anchor, two levels up from the real committed wrapper View the descriptor builds, and needs
|
|
5
|
-
// its OWN anchorHostStyle merge (see shared.ts's `descriptor` getter). Mirrors pressable.test.ts's
|
|
6
|
-
// "resolves a class=" case.
|
|
7
|
-
import '@angular/compiler';
|
|
8
|
-
import { Component } from '@angular/core';
|
|
9
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
10
|
-
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
11
|
-
import { installFabric } from '@symbiote-native/test-utils';
|
|
12
|
-
|
|
13
|
-
import { mount, unmount } from '../../render';
|
|
14
|
-
import { ActivityIndicator } from './index';
|
|
15
|
-
|
|
16
|
-
const ROOT_TAG = 909;
|
|
17
|
-
const fabric = installFabric();
|
|
18
|
-
|
|
19
|
-
@Component({
|
|
20
|
-
selector: 'symbiote-activity-indicator-class-host',
|
|
21
|
-
standalone: true,
|
|
22
|
-
imports: [ActivityIndicator],
|
|
23
|
-
template: `<ActivityIndicator class="card" />`,
|
|
24
|
-
})
|
|
25
|
-
class ActivityIndicatorClassHost {}
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
fabric.reset();
|
|
29
|
-
registerStyles({ card: { backgroundColor: 'red' } });
|
|
30
|
-
});
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
unmount(ROOT_TAG);
|
|
33
|
-
clearGlobalStyles();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('ActivityIndicator anchor class= resolution', () => {
|
|
37
|
-
it('resolves a class= on the ActivityIndicator use site onto the real committed wrapper View', async () => {
|
|
38
|
-
mount(ROOT_TAG, ActivityIndicatorClassHost);
|
|
39
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
40
|
-
|
|
41
|
-
const node = fabric.find(n => n.props.backgroundColor === 'red');
|
|
42
|
-
expect(node, 'a real Fabric node carries the class-derived style').toBeDefined();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import '@angular/compiler';
|
|
2
|
-
import { Component, signal } from '@angular/core';
|
|
3
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
5
|
-
|
|
6
|
-
import { mount, unmount } from '../../render';
|
|
7
|
-
import { ActivityIndicator } from './index';
|
|
8
|
-
|
|
9
|
-
const ROOT_TAG = 905;
|
|
10
|
-
const fabric = installFabric();
|
|
11
|
-
|
|
12
|
-
let capturedHost: ActivityIndicatorHost | undefined;
|
|
13
|
-
|
|
14
|
-
function findCommitted(predicate: (node: IFakeNode) => boolean): IFakeNode | undefined {
|
|
15
|
-
const visit = (node: IFakeNode): IFakeNode | undefined => {
|
|
16
|
-
if (predicate(node)) return node;
|
|
17
|
-
for (const child of node.children) {
|
|
18
|
-
const found = visit(child);
|
|
19
|
-
if (found) return found;
|
|
20
|
-
}
|
|
21
|
-
return undefined;
|
|
22
|
-
};
|
|
23
|
-
for (const node of fabric.committed) {
|
|
24
|
-
const found = visit(node);
|
|
25
|
-
if (found) return found;
|
|
26
|
-
}
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function findSpinner(): IFakeNode {
|
|
31
|
-
const node = findCommitted(n => n.viewName === 'ActivityIndicatorView');
|
|
32
|
-
if (!node) throw new Error('no ActivityIndicatorView was created');
|
|
33
|
-
return node;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function findWrapper(): IFakeNode {
|
|
37
|
-
const node = findCommitted(n => n.viewName === 'RCTView' && n.props.pointerEvents !== 'box-none');
|
|
38
|
-
if (!node) throw new Error('no ActivityIndicator wrapper was created');
|
|
39
|
-
return node;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@Component({
|
|
43
|
-
selector: 'symbiote-activity-indicator-host',
|
|
44
|
-
standalone: true,
|
|
45
|
-
imports: [ActivityIndicator],
|
|
46
|
-
template: `
|
|
47
|
-
<ActivityIndicator
|
|
48
|
-
[size]="size()"
|
|
49
|
-
[color]="color()"
|
|
50
|
-
[animating]="animating()"
|
|
51
|
-
[testID]="testID()"
|
|
52
|
-
[accessible]="true"
|
|
53
|
-
[accessibilityLabel]="'loading'"
|
|
54
|
-
(layout)="onLayout($event)"
|
|
55
|
-
/>
|
|
56
|
-
`,
|
|
57
|
-
})
|
|
58
|
-
class ActivityIndicatorHost {
|
|
59
|
-
readonly size = signal<'small' | 'large' | number>('large');
|
|
60
|
-
readonly color = signal('#0000ff');
|
|
61
|
-
readonly animating = signal(false);
|
|
62
|
-
readonly testID = signal('spinner-wrapper');
|
|
63
|
-
onLayout = vi.fn();
|
|
64
|
-
|
|
65
|
-
constructor() {
|
|
66
|
-
// Captures the live component instance so the test can drive its signals after mount.
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
68
|
-
capturedHost = this;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
beforeEach(() => {
|
|
73
|
-
capturedHost = undefined;
|
|
74
|
-
fabric.reset();
|
|
75
|
-
});
|
|
76
|
-
afterEach(() => unmount(ROOT_TAG));
|
|
77
|
-
|
|
78
|
-
describe('ActivityIndicator', () => {
|
|
79
|
-
it('renders through the shared descriptor shape and forwards wrapper props', async () => {
|
|
80
|
-
mount(ROOT_TAG, ActivityIndicatorHost);
|
|
81
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
82
|
-
|
|
83
|
-
expect(fabric.serialize(fabric.appRoot().children)).toBe('RCTView(ActivityIndicatorView)');
|
|
84
|
-
|
|
85
|
-
const spinner = findSpinner();
|
|
86
|
-
expect(spinner.props.animating).toBe(false);
|
|
87
|
-
expect(spinner.props.color).toBe('#0000ff');
|
|
88
|
-
expect(spinner.props.hidesWhenStopped).toBe(true);
|
|
89
|
-
expect(spinner.props.size).toBe('large');
|
|
90
|
-
expect(spinner.props.width).toBe(36);
|
|
91
|
-
expect(spinner.props.height).toBe(36);
|
|
92
|
-
|
|
93
|
-
const wrapper = findWrapper();
|
|
94
|
-
expect(wrapper.props.testID).toBe('spinner-wrapper');
|
|
95
|
-
expect(wrapper.props.accessible).toBe(true);
|
|
96
|
-
expect(wrapper.props.accessibilityLabel).toBe('loading');
|
|
97
|
-
expect(wrapper.props.alignItems).toBe('center');
|
|
98
|
-
expect(wrapper.props.justifyContent).toBe('center');
|
|
99
|
-
|
|
100
|
-
fabric.fireEvent(wrapper.instanceHandle, 'topLayout', {});
|
|
101
|
-
expect(capturedHost?.onLayout).toHaveBeenCalledOnce();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('patches the descriptor without remounting the native wrapper', async () => {
|
|
105
|
-
mount(ROOT_TAG, ActivityIndicatorHost);
|
|
106
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
107
|
-
|
|
108
|
-
const before = findWrapper();
|
|
109
|
-
const createdBefore = fabric.counts.createNode;
|
|
110
|
-
|
|
111
|
-
if (!capturedHost) throw new Error('host was not captured');
|
|
112
|
-
capturedHost.size.set(48);
|
|
113
|
-
capturedHost.color.set('#ff0000');
|
|
114
|
-
capturedHost.animating.set(true);
|
|
115
|
-
capturedHost.testID.set('spinner-wrapper-updated');
|
|
116
|
-
await Promise.resolve();
|
|
117
|
-
await new Promise<void>(resolve => setTimeout(resolve, 0));
|
|
118
|
-
|
|
119
|
-
const after = findWrapper();
|
|
120
|
-
expect(after.instanceHandle).toBe(before.instanceHandle);
|
|
121
|
-
expect(fabric.counts.createNode).toBe(createdBefore);
|
|
122
|
-
expect(after.props.testID).toBe('spinner-wrapper-updated');
|
|
123
|
-
const spinner = findSpinner();
|
|
124
|
-
expect(spinner.props.color).toBe('#ff0000');
|
|
125
|
-
expect(spinner.props.animating).toBe(true);
|
|
126
|
-
expect(spinner.props.width).toBe(48);
|
|
127
|
-
expect(spinner.props.height).toBe(48);
|
|
128
|
-
// Updating from string size to numeric size sends null to reset the previous native enum.
|
|
129
|
-
expect(spinner.props.size).toBeNull();
|
|
130
|
-
});
|
|
131
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// Regression guard: columnWrapperStyle previously accepted ONLY a JS style object/array. It now
|
|
2
|
-
// also resolves a class-name string through the shared style registry, merged onto the same
|
|
3
|
-
// flex-row wrapper as an object/array value (see index.ts's ngOnChanges). Mirrors the Vue twin
|
|
4
|
-
// (column-wrapper-style-class.test.ts) and flat-list.test.ts's row-packing shape.
|
|
5
|
-
import '@angular/compiler';
|
|
6
|
-
import { Component } from '@angular/core';
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
|
-
import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
|
|
9
|
-
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
10
|
-
|
|
11
|
-
import { mount, unmount } from '../../render';
|
|
12
|
-
import { FlatList } from './index';
|
|
13
|
-
import { VListItemDirective } from '../virtualized-list/directives';
|
|
14
|
-
|
|
15
|
-
const ROOT_TAG = 953;
|
|
16
|
-
const fabric = installFabric();
|
|
17
|
-
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
18
|
-
|
|
19
|
-
interface IRow {
|
|
20
|
-
id: string;
|
|
21
|
-
label: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const rows: IRow[] = Array.from({ length: 4 }, (_unused, index) => ({
|
|
25
|
-
id: `r-${index}`,
|
|
26
|
-
label: `row-${index}`,
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
function walk(nodes: IFakeNode[], visit: (node: IFakeNode) => void): void {
|
|
30
|
-
for (const node of nodes) {
|
|
31
|
-
visit(node);
|
|
32
|
-
walk(node.children, visit);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function rowsWithFlexDirection(): IFakeNode[] {
|
|
37
|
-
const found: IFakeNode[] = [];
|
|
38
|
-
walk(fabric.committed, node => {
|
|
39
|
-
if (node.viewName === 'RCTView' && node.props.flexDirection === 'row') found.push(node);
|
|
40
|
-
});
|
|
41
|
-
return found;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
@Component({
|
|
45
|
-
selector: 'symbiote-flatlist-column-style-class-host',
|
|
46
|
-
standalone: true,
|
|
47
|
-
imports: [FlatList, VListItemDirective],
|
|
48
|
-
template: `
|
|
49
|
-
<FlatList
|
|
50
|
-
[data]="rows"
|
|
51
|
-
[numColumns]="2"
|
|
52
|
-
[keyExtractor]="keyExtractor"
|
|
53
|
-
[columnWrapperStyle]="'gap8'"
|
|
54
|
-
>
|
|
55
|
-
<ng-template vListItem let-item>
|
|
56
|
-
<symbiote-text [testID]="item.id">{{ item.label }}</symbiote-text>
|
|
57
|
-
</ng-template>
|
|
58
|
-
</FlatList>
|
|
59
|
-
`,
|
|
60
|
-
})
|
|
61
|
-
class FlatListColumnStyleClassHost {
|
|
62
|
-
rows = rows;
|
|
63
|
-
keyExtractor = (item: IRow): string => item.id;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@Component({
|
|
67
|
-
selector: 'symbiote-flatlist-column-style-object-host',
|
|
68
|
-
standalone: true,
|
|
69
|
-
imports: [FlatList, VListItemDirective],
|
|
70
|
-
template: `
|
|
71
|
-
<FlatList
|
|
72
|
-
[data]="rows"
|
|
73
|
-
[numColumns]="2"
|
|
74
|
-
[keyExtractor]="keyExtractor"
|
|
75
|
-
[columnWrapperStyle]="{ gap: 4 }"
|
|
76
|
-
>
|
|
77
|
-
<ng-template vListItem let-item>
|
|
78
|
-
<symbiote-text [testID]="item.id">{{ item.label }}</symbiote-text>
|
|
79
|
-
</ng-template>
|
|
80
|
-
</FlatList>
|
|
81
|
-
`,
|
|
82
|
-
})
|
|
83
|
-
class FlatListColumnStyleObjectHost {
|
|
84
|
-
rows = rows;
|
|
85
|
-
keyExtractor = (item: IRow): string => item.id;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
beforeEach(() => {
|
|
89
|
-
fabric.reset();
|
|
90
|
-
registerStyles({ gap8: { gap: 8 } });
|
|
91
|
-
});
|
|
92
|
-
afterEach(() => {
|
|
93
|
-
unmount(ROOT_TAG);
|
|
94
|
-
clearGlobalStyles();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('FlatList columnWrapperStyle class-name support', () => {
|
|
98
|
-
it('resolves a class-name string onto the row wrapper alongside flexDirection', async () => {
|
|
99
|
-
mount(ROOT_TAG, FlatListColumnStyleClassHost);
|
|
100
|
-
await tick();
|
|
101
|
-
await tick();
|
|
102
|
-
|
|
103
|
-
const found = rowsWithFlexDirection();
|
|
104
|
-
expect(found.length, 'two flex-row rows for 4 items in 2 columns').toBe(2);
|
|
105
|
-
expect(found[0].props.gap).toBe(8);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('still accepts an ordinary style object unchanged', async () => {
|
|
109
|
-
mount(ROOT_TAG, FlatListColumnStyleObjectHost);
|
|
110
|
-
await tick();
|
|
111
|
-
await tick();
|
|
112
|
-
|
|
113
|
-
const found = rowsWithFlexDirection();
|
|
114
|
-
expect(found[0].props.gap).toBe(4);
|
|
115
|
-
});
|
|
116
|
-
});
|