@symbiote-native/angular 0.0.0-canary-20260724081123

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.
Files changed (276) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/babel-linker.cjs +7 -0
  4. package/babel-register-composed.cjs +127 -0
  5. package/bin/symbiote-angular-dev.cjs +93 -0
  6. package/build/angular/anchor-host-registry.d.ts +2 -0
  7. package/build/angular/anchor-host-registry.js +84 -0
  8. package/build/angular/anchor-host-registry.js.map +1 -0
  9. package/build/angular/bootstrap.d.ts +7 -0
  10. package/build/angular/bootstrap.js +15 -0
  11. package/build/angular/bootstrap.js.map +1 -0
  12. package/build/angular/components/activity-indicator/index.android.d.ts +12 -0
  13. package/build/angular/components/activity-indicator/index.android.js +21 -0
  14. package/build/angular/components/activity-indicator/index.android.js.map +1 -0
  15. package/build/angular/components/activity-indicator/index.d.ts +1 -0
  16. package/build/angular/components/activity-indicator/index.ios.d.ts +8 -0
  17. package/build/angular/components/activity-indicator/index.ios.js +21 -0
  18. package/build/angular/components/activity-indicator/index.ios.js.map +1 -0
  19. package/build/angular/components/activity-indicator/index.js +4 -0
  20. package/build/angular/components/activity-indicator/index.js.map +1 -0
  21. package/build/angular/components/activity-indicator/shared.d.ts +67 -0
  22. package/build/angular/components/activity-indicator/shared.js +260 -0
  23. package/build/angular/components/activity-indicator/shared.js.map +1 -0
  24. package/build/angular/components/button.d.ts +63 -0
  25. package/build/angular/components/button.js +332 -0
  26. package/build/angular/components/button.js.map +1 -0
  27. package/build/angular/components/flat-list/index.d.ts +122 -0
  28. package/build/angular/components/flat-list/index.js +688 -0
  29. package/build/angular/components/flat-list/index.js.map +1 -0
  30. package/build/angular/components/image/index.android.d.ts +10 -0
  31. package/build/angular/components/image/index.android.js +59 -0
  32. package/build/angular/components/image/index.android.js.map +1 -0
  33. package/build/angular/components/image/index.d.ts +1 -0
  34. package/build/angular/components/image/index.ios.d.ts +10 -0
  35. package/build/angular/components/image/index.ios.js +59 -0
  36. package/build/angular/components/image/index.ios.js.map +1 -0
  37. package/build/angular/components/image/index.js +3 -0
  38. package/build/angular/components/image/index.js.map +1 -0
  39. package/build/angular/components/image/shared.d.ts +101 -0
  40. package/build/angular/components/image/shared.js +406 -0
  41. package/build/angular/components/image/shared.js.map +1 -0
  42. package/build/angular/components/image-background.d.ts +81 -0
  43. package/build/angular/components/image-background.js +418 -0
  44. package/build/angular/components/image-background.js.map +1 -0
  45. package/build/angular/components/input-accessory-view/index.d.ts +60 -0
  46. package/build/angular/components/input-accessory-view/index.js +251 -0
  47. package/build/angular/components/input-accessory-view/index.js.map +1 -0
  48. package/build/angular/components/keyboard-avoiding-view/index.d.ts +82 -0
  49. package/build/angular/components/keyboard-avoiding-view/index.js +358 -0
  50. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -0
  51. package/build/angular/components/modal/index.d.ts +92 -0
  52. package/build/angular/components/modal/index.js +360 -0
  53. package/build/angular/components/modal/index.js.map +1 -0
  54. package/build/angular/components/pressable/index.d.ts +123 -0
  55. package/build/angular/components/pressable/index.js +420 -0
  56. package/build/angular/components/pressable/index.js.map +1 -0
  57. package/build/angular/components/refresh-control/index.d.ts +86 -0
  58. package/build/angular/components/refresh-control/index.js +342 -0
  59. package/build/angular/components/refresh-control/index.js.map +1 -0
  60. package/build/angular/components/safe-area-view/index.d.ts +57 -0
  61. package/build/angular/components/safe-area-view/index.js +248 -0
  62. package/build/angular/components/safe-area-view/index.js.map +1 -0
  63. package/build/angular/components/scroll-view/index.android.d.ts +21 -0
  64. package/build/angular/components/scroll-view/index.android.js +235 -0
  65. package/build/angular/components/scroll-view/index.android.js.map +1 -0
  66. package/build/angular/components/scroll-view/index.d.ts +3 -0
  67. package/build/angular/components/scroll-view/index.ios.d.ts +9 -0
  68. package/build/angular/components/scroll-view/index.ios.js +86 -0
  69. package/build/angular/components/scroll-view/index.ios.js.map +1 -0
  70. package/build/angular/components/scroll-view/index.js +6 -0
  71. package/build/angular/components/scroll-view/index.js.map +1 -0
  72. package/build/angular/components/scroll-view/projection.d.ts +32 -0
  73. package/build/angular/components/scroll-view/projection.js +334 -0
  74. package/build/angular/components/scroll-view/projection.js.map +1 -0
  75. package/build/angular/components/scroll-view/shared.d.ts +230 -0
  76. package/build/angular/components/scroll-view/shared.js +682 -0
  77. package/build/angular/components/scroll-view/shared.js.map +1 -0
  78. package/build/angular/components/scroll-view/sticky-header.d.ts +40 -0
  79. package/build/angular/components/scroll-view/sticky-header.js +225 -0
  80. package/build/angular/components/scroll-view/sticky-header.js.map +1 -0
  81. package/build/angular/components/section-list/index.d.ts +116 -0
  82. package/build/angular/components/section-list/index.js +641 -0
  83. package/build/angular/components/section-list/index.js.map +1 -0
  84. package/build/angular/components/switch/index.android.d.ts +20 -0
  85. package/build/angular/components/switch/index.android.js +70 -0
  86. package/build/angular/components/switch/index.android.js.map +1 -0
  87. package/build/angular/components/switch/index.d.ts +1 -0
  88. package/build/angular/components/switch/index.ios.d.ts +19 -0
  89. package/build/angular/components/switch/index.ios.js +69 -0
  90. package/build/angular/components/switch/index.ios.js.map +1 -0
  91. package/build/angular/components/switch/index.js +4 -0
  92. package/build/angular/components/switch/index.js.map +1 -0
  93. package/build/angular/components/switch/shared.d.ts +40 -0
  94. package/build/angular/components/switch/shared.js +127 -0
  95. package/build/angular/components/switch/shared.js.map +1 -0
  96. package/build/angular/components/text-input.d.ts +153 -0
  97. package/build/angular/components/text-input.js +599 -0
  98. package/build/angular/components/text-input.js.map +1 -0
  99. package/build/angular/components/touchable/index.d.ts +245 -0
  100. package/build/angular/components/touchable/index.js +1186 -0
  101. package/build/angular/components/touchable/index.js.map +1 -0
  102. package/build/angular/components/touchable-native-feedback/index.d.ts +83 -0
  103. package/build/angular/components/touchable-native-feedback/index.js +371 -0
  104. package/build/angular/components/touchable-native-feedback/index.js.map +1 -0
  105. package/build/angular/components/virtualized-list/directives.d.ts +54 -0
  106. package/build/angular/components/virtualized-list/directives.js +153 -0
  107. package/build/angular/components/virtualized-list/directives.js.map +1 -0
  108. package/build/angular/components/virtualized-list/index.d.ts +253 -0
  109. package/build/angular/components/virtualized-list/index.js +1031 -0
  110. package/build/angular/components/virtualized-list/index.js.map +1 -0
  111. package/build/angular/components/virtualized-section-list/directives.d.ts +37 -0
  112. package/build/angular/components/virtualized-section-list/directives.js +76 -0
  113. package/build/angular/components/virtualized-section-list/directives.js.map +1 -0
  114. package/build/angular/components/virtualized-section-list/index.d.ts +178 -0
  115. package/build/angular/components/virtualized-section-list/index.js +720 -0
  116. package/build/angular/components/virtualized-section-list/index.js.map +1 -0
  117. package/build/angular/components.d.ts +39 -0
  118. package/build/angular/components.js +38 -0
  119. package/build/angular/components.js.map +1 -0
  120. package/build/angular/create-portal/index.d.ts +22 -0
  121. package/build/angular/create-portal/index.js +74 -0
  122. package/build/angular/create-portal/index.js.map +1 -0
  123. package/build/angular/create-tunnel/index.d.ts +34 -0
  124. package/build/angular/create-tunnel/index.js +126 -0
  125. package/build/angular/create-tunnel/index.js.map +1 -0
  126. package/build/angular/descriptor-to-angular/index.d.ts +22 -0
  127. package/build/angular/descriptor-to-angular/index.js +142 -0
  128. package/build/angular/descriptor-to-angular/index.js.map +1 -0
  129. package/build/angular/host-instance/index.d.ts +2 -0
  130. package/build/angular/host-instance/index.js +37 -0
  131. package/build/angular/host-instance/index.js.map +1 -0
  132. package/build/angular/index.d.ts +22 -0
  133. package/build/angular/index.js +46 -0
  134. package/build/angular/index.js.map +1 -0
  135. package/build/angular/modules/animated/animated-leaf-binder/index.d.ts +14 -0
  136. package/build/angular/modules/animated/animated-leaf-binder/index.js +91 -0
  137. package/build/angular/modules/animated/animated-leaf-binder/index.js.map +1 -0
  138. package/build/angular/modules/animated/animated-leaf-binder.d.ts +14 -0
  139. package/build/angular/modules/animated/animated-leaf-binder.js +91 -0
  140. package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -0
  141. package/build/angular/modules/animated/create-animated-component.d.ts +59 -0
  142. package/build/angular/modules/animated/create-animated-component.js +364 -0
  143. package/build/angular/modules/animated/create-animated-component.js.map +1 -0
  144. package/build/angular/modules/animated/index.d.ts +51 -0
  145. package/build/angular/modules/animated/index.js +59 -0
  146. package/build/angular/modules/animated/index.js.map +1 -0
  147. package/build/angular/modules/app-registry/index.d.ts +7 -0
  148. package/build/angular/modules/app-registry/index.js +23 -0
  149. package/build/angular/modules/app-registry/index.js.map +1 -0
  150. package/build/angular/modules/status-bar/index.d.ts +20 -0
  151. package/build/angular/modules/status-bar/index.js +59 -0
  152. package/build/angular/modules/status-bar/index.js.map +1 -0
  153. package/build/angular/primitives/index.d.ts +63 -0
  154. package/build/angular/primitives/index.js +189 -0
  155. package/build/angular/primitives/index.js.map +1 -0
  156. package/build/angular/primitives/shared.d.ts +88 -0
  157. package/build/angular/primitives/shared.js +171 -0
  158. package/build/angular/primitives/shared.js.map +1 -0
  159. package/build/angular/render/index.d.ts +11 -0
  160. package/build/angular/render/index.js +171 -0
  161. package/build/angular/render/index.js.map +1 -0
  162. package/build/angular/renderer/index.d.ts +37 -0
  163. package/build/angular/renderer/index.js +301 -0
  164. package/build/angular/renderer/index.js.map +1 -0
  165. package/build/angular/services/color-scheme.service.d.ts +16 -0
  166. package/build/angular/services/color-scheme.service.js +31 -0
  167. package/build/angular/services/color-scheme.service.js.map +1 -0
  168. package/build/angular/services/index.d.ts +2 -0
  169. package/build/angular/services/index.js +3 -0
  170. package/build/angular/services/index.js.map +1 -0
  171. package/build/angular/services/window-dimensions.service.d.ts +16 -0
  172. package/build/angular/services/window-dimensions.service.js +42 -0
  173. package/build/angular/services/window-dimensions.service.js.map +1 -0
  174. package/metro-config.cjs +48 -0
  175. package/metro-css-parser.cjs +13 -0
  176. package/package.json +80 -0
  177. package/src/__tests__/responder-change-detection.test.ts +102 -0
  178. package/src/__tests__/responder-nested-cd.test.ts +113 -0
  179. package/src/anchor-host-registry.ts +88 -0
  180. package/src/angular-gaps.test.ts +142 -0
  181. package/src/angular-imports.test.ts +100 -0
  182. package/src/bootstrap.ts +26 -0
  183. package/src/components/activity-indicator/activity-indicator-class-style.test.ts +44 -0
  184. package/src/components/activity-indicator/activity-indicator.test.ts +131 -0
  185. package/src/components/activity-indicator/index.android.ts +16 -0
  186. package/src/components/activity-indicator/index.ios.ts +17 -0
  187. package/src/components/activity-indicator/index.ts +4 -0
  188. package/src/components/activity-indicator/shared.ts +206 -0
  189. package/src/components/button.ts +193 -0
  190. package/src/components/flat-list/column-wrapper-style-class.test.ts +116 -0
  191. package/src/components/flat-list/flat-list-array-style.test.ts +131 -0
  192. package/src/components/flat-list/flat-list-class-style.test.ts +74 -0
  193. package/src/components/flat-list/flat-list-scroll-containment.test.ts +107 -0
  194. package/src/components/flat-list/flat-list.test.ts +99 -0
  195. package/src/components/flat-list/index.ts +574 -0
  196. package/src/components/image/image-class-style.test.ts +46 -0
  197. package/src/components/image/index.android.ts +48 -0
  198. package/src/components/image/index.ios.ts +48 -0
  199. package/src/components/image/index.ts +3 -0
  200. package/src/components/image/shared.ts +479 -0
  201. package/src/components/image-background-class-style.test.ts +48 -0
  202. package/src/components/image-background-style-class.test.ts +79 -0
  203. package/src/components/image-background.ts +270 -0
  204. package/src/components/input-accessory-view/index.ts +185 -0
  205. package/src/components/input-accessory-view/input-accessory-view.test.ts +43 -0
  206. package/src/components/keyboard-avoiding-view/index.ts +303 -0
  207. package/src/components/keyboard-avoiding-view/keyboard-avoiding-view.test.ts +54 -0
  208. package/src/components/modal/index.ts +299 -0
  209. package/src/components/modal/modal.test.ts +43 -0
  210. package/src/components/pressable/index.ts +373 -0
  211. package/src/components/pressable/pressable.test.ts +85 -0
  212. package/src/components/refresh-control/index.ts +284 -0
  213. package/src/components/refresh-control/refresh-control.test.ts +41 -0
  214. package/src/components/safe-area-view/index.ts +177 -0
  215. package/src/components/safe-area-view/safe-area-view.test.ts +43 -0
  216. package/src/components/scroll-view/android-scroll-view-axis-projection.test.ts +123 -0
  217. package/src/components/scroll-view/content-container-style-class.test.ts +75 -0
  218. package/src/components/scroll-view/index.android.ts +190 -0
  219. package/src/components/scroll-view/index.ios.ts +79 -0
  220. package/src/components/scroll-view/index.ts +7 -0
  221. package/src/components/scroll-view/projection.ts +414 -0
  222. package/src/components/scroll-view/scroll-view-class-style.test.ts +134 -0
  223. package/src/components/scroll-view/scroll-view-projection.test.ts +194 -0
  224. package/src/components/scroll-view/shared.ts +844 -0
  225. package/src/components/scroll-view/sticky-header.ts +255 -0
  226. package/src/components/section-list/index.ts +445 -0
  227. package/src/components/switch/android-switch-class.test.ts +42 -0
  228. package/src/components/switch/index.android.ts +61 -0
  229. package/src/components/switch/index.ios.ts +60 -0
  230. package/src/components/switch/index.ts +4 -0
  231. package/src/components/switch/shared.ts +197 -0
  232. package/src/components/switch/switch.test.ts +92 -0
  233. package/src/components/text-input-class-style.test.ts +54 -0
  234. package/src/components/text-input.ts +531 -0
  235. package/src/components/touchable/index.ts +629 -0
  236. package/src/components/touchable/touchable.test.ts +102 -0
  237. package/src/components/touchable-native-feedback/index.ts +254 -0
  238. package/src/components/touchable-native-feedback/touchable-native-feedback.test.ts +49 -0
  239. package/src/components/virtualized-list/directives.ts +168 -0
  240. package/src/components/virtualized-list/index.ts +992 -0
  241. package/src/components/virtualized-list/virtualized-list-class-style.test.ts +76 -0
  242. package/src/components/virtualized-section-list/directives.ts +90 -0
  243. package/src/components/virtualized-section-list/index.ts +601 -0
  244. package/src/components/virtualized-section-list/virtualized-section-list-class-style.test.ts +60 -0
  245. package/src/components.ts +149 -0
  246. package/src/create-portal/create-portal.test.ts +90 -0
  247. package/src/create-portal/index.ts +74 -0
  248. package/src/create-tunnel/create-tunnel.test.ts +120 -0
  249. package/src/create-tunnel/index.ts +136 -0
  250. package/src/descriptor-to-angular/descriptor-outlet.test.ts +94 -0
  251. package/src/descriptor-to-angular/index.ts +190 -0
  252. package/src/host-instance/host-instance.test.ts +49 -0
  253. package/src/host-instance/index.ts +36 -0
  254. package/src/index.ts +261 -0
  255. package/src/modules/animated/animated-leaf-binder/animated-leaf-binder.test.ts +116 -0
  256. package/src/modules/animated/animated-leaf-binder/index.ts +101 -0
  257. package/src/modules/animated/animated-leaf-binder.test.ts +116 -0
  258. package/src/modules/animated/animated-leaf-binder.ts +101 -0
  259. package/src/modules/animated/animated-scroll-view.test.ts +102 -0
  260. package/src/modules/animated/create-animated-component.ts +365 -0
  261. package/src/modules/animated/index.ts +105 -0
  262. package/src/modules/app-registry/app-registry.test.ts +106 -0
  263. package/src/modules/app-registry/index.ts +60 -0
  264. package/src/modules/status-bar/index.ts +57 -0
  265. package/src/modules/status-bar/status-bar.test.ts +50 -0
  266. package/src/primitives/index.ts +119 -0
  267. package/src/primitives/shared.ts +190 -0
  268. package/src/render/index.ts +235 -0
  269. package/src/render/render.test.ts +147 -0
  270. package/src/renderer/index.ts +347 -0
  271. package/src/renderer/renderer.test.ts +203 -0
  272. package/src/services/color-scheme.service.ts +27 -0
  273. package/src/services/index.ts +2 -0
  274. package/src/services/services.test.ts +163 -0
  275. package/src/services/window-dimensions.service.ts +41 -0
  276. package/tsconfig.angular.base.json +30 -0
@@ -0,0 +1,79 @@
1
+ // Regression guard: imageStyle previously accepted ONLY a JS style object/array. It now also
2
+ // resolves a class-name string through the shared style registry, same as the `class` prop fix on
3
+ // the WRAPPER view (see image-background-class-style.test.ts) — and must land on the INNER image,
4
+ // never the wrapper, exactly like that prior fix. Mirrors the Vue twin
5
+ // (image-background-style-class.test.ts).
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, type IFakeNode } from '@symbiote-native/test-utils';
11
+
12
+ import { mount, unmount } from '../render';
13
+ import { ImageBackground } from './image-background';
14
+
15
+ const ROOT_TAG = 917;
16
+ const fabric = installFabric();
17
+ const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
18
+
19
+ function committedImage(): IFakeNode {
20
+ const node = fabric.find(n => n.viewName === 'RCTImageView');
21
+ expect(node, 'the inner image was committed').toBeDefined();
22
+ if (node === undefined) throw new Error('unreachable: image missing');
23
+ return node;
24
+ }
25
+
26
+ function committedWrapper(): IFakeNode {
27
+ const node = fabric.find(n => n.viewName === 'RCTView');
28
+ expect(node, 'the wrapper view was committed').toBeDefined();
29
+ if (node === undefined) throw new Error('unreachable: wrapper missing');
30
+ return node;
31
+ }
32
+
33
+ @Component({
34
+ selector: 'symbiote-image-background-style-class-host',
35
+ standalone: true,
36
+ imports: [ImageBackground],
37
+ template: `<ImageBackground
38
+ [source]="{ uri: 'https://example.com/a.png' }"
39
+ [imageStyle]="'tinted'"
40
+ />`,
41
+ })
42
+ class ImageBackgroundStyleClassHost {}
43
+
44
+ @Component({
45
+ selector: 'symbiote-image-background-style-object-host',
46
+ standalone: true,
47
+ imports: [ImageBackground],
48
+ template: `<ImageBackground
49
+ [source]="{ uri: 'https://example.com/a.png' }"
50
+ [imageStyle]="{ opacity: 0.25 }"
51
+ />`,
52
+ })
53
+ class ImageBackgroundStyleObjectHost {}
54
+
55
+ beforeEach(() => {
56
+ fabric.reset();
57
+ registerStyles({ tinted: { opacity: 0.5 } });
58
+ });
59
+ afterEach(() => {
60
+ unmount(ROOT_TAG);
61
+ clearGlobalStyles();
62
+ });
63
+
64
+ describe('ImageBackground imageStyle class-name support', () => {
65
+ it('resolves a class-name string onto the inner image, not the wrapper', async () => {
66
+ mount(ROOT_TAG, ImageBackgroundStyleClassHost);
67
+ await tick();
68
+
69
+ expect(committedImage().props.opacity).toBe(0.5);
70
+ expect(committedWrapper().props.opacity).toBeUndefined();
71
+ });
72
+
73
+ it('still accepts an ordinary style object unchanged', async () => {
74
+ mount(ROOT_TAG, ImageBackgroundStyleObjectHost);
75
+ await tick();
76
+
77
+ expect(committedImage().props.opacity).toBe(0.25);
78
+ });
79
+ });
@@ -0,0 +1,270 @@
1
+ // ImageBackground: the Angular lifecycle half. ImageBackground has NO native host of its own —
2
+ // it composes an outer View, an absolute-fill Image behind the user children, and the children
3
+ // on top. The composition math (the absolute-fill, the wrapper-dimension proxy onto the inner
4
+ // image, the imageStyle merge) lives framework-agnostic in @symbiote-native/components/renderImageBackground
5
+ // and is shared verbatim with React/Vue. React/Vue bridge the Descriptor it returns; Angular has
6
+ // no hyperscript bridge, so it composes with a TEMPLATE instead and reuses renderImageBackground
7
+ // ONLY to compute the inner image's merged style (read off the tree it returns) — the math stays
8
+ // shared, never reimplemented. The inner <Image> is the adapter's own Image component, which owns
9
+ // source resolution / a11y folding / event emission; ImageBackground forwards every Image prop onto
10
+ // it. `style` is the WRAPPER View style; `imageStyle` targets the inner image. Children paint AFTER
11
+ // the image (on top) via <ng-content>. The Angular twin of the React/Vue ImageBackground.
12
+
13
+ import {
14
+ CUSTOM_ELEMENTS_SCHEMA,
15
+ ChangeDetectionStrategy,
16
+ Component,
17
+ ElementRef,
18
+ EventEmitter,
19
+ inject,
20
+ Input,
21
+ Output,
22
+ } from '@angular/core';
23
+ import {
24
+ renderImageBackground,
25
+ resolveAccessibilityProps,
26
+ type IAccessibilityProps,
27
+ type IAccessibilityStateValue,
28
+ type IAriaProps,
29
+ type IImageProps,
30
+ type IImageSourceProp,
31
+ type IResizeMode,
32
+ } from '@symbiote-native/components';
33
+ import {
34
+ resolveClassName,
35
+ type IStyleProp,
36
+ type ISymbioteEvent,
37
+ type IViewStyle,
38
+ } from '@symbiote-native/engine';
39
+ import { Image } from './image';
40
+ import { anchorHostStyle, ViewHost } from '../primitives';
41
+
42
+ // Mirrors React's IImageBackgroundProps minus children (Angular takes children via <ng-content>):
43
+ // every forwarding Image prop flows onto the inner image; `style` is the WRAPPER View style and
44
+ // `imageStyle` the inner image's.
45
+ export interface IAngularImageBackgroundProps extends Omit<IImageProps, 'style'> {
46
+ style?: IStyleProp<IViewStyle>;
47
+ // A bare string resolves through the shared style registry, like `class` on the wrapper.
48
+ imageStyle?: IStyleProp<IViewStyle> | string;
49
+ }
50
+
51
+ // What the ImageBackground component itself takes as plain @Input()s: the full surface minus the
52
+ // inner Image's callback-shaped events, which it exposes as real @Output() EventEmitters instead
53
+ // (re-emitting the inner <Image>'s own already-converted outputs — see the template below).
54
+ export type IAngularImageBackgroundInputs = Omit<
55
+ IAngularImageBackgroundProps,
56
+ | 'onAccessibilityAction'
57
+ | 'onAccessibilityTap'
58
+ | 'onMagicTap'
59
+ | 'onAccessibilityEscape'
60
+ | 'onLoadStart'
61
+ | 'onLoad'
62
+ | 'onLoadEnd'
63
+ | 'onError'
64
+ | 'onProgress'
65
+ | 'onPartialLoad'
66
+ >;
67
+
68
+ @Component({
69
+ selector: 'ImageBackground',
70
+ standalone: true,
71
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
72
+ imports: [Image, ViewHost],
73
+ changeDetection: ChangeDetectionStrategy.OnPush,
74
+ template: `
75
+ <symbiote-view [style]="wrapperStyle">
76
+ <Image
77
+ [source]="source"
78
+ [defaultSource]="defaultSource"
79
+ [loadingIndicatorSource]="loadingIndicatorSource"
80
+ [style]="imageStyle"
81
+ [resizeMode]="resizeMode"
82
+ [resizeMethod]="resizeMethod"
83
+ [tintColor]="tintColor"
84
+ [blurRadius]="blurRadius"
85
+ [capInsets]="capInsets"
86
+ [fadeDuration]="fadeDuration"
87
+ [progressiveRenderingEnabled]="progressiveRenderingEnabled"
88
+ [src]="src"
89
+ [srcSet]="srcSet"
90
+ [alt]="alt"
91
+ [width]="width"
92
+ [height]="height"
93
+ [crossOrigin]="crossOrigin"
94
+ [referrerPolicy]="referrerPolicy"
95
+ [testID]="testID"
96
+ [nativeID]="nativeID"
97
+ [accessible]="accessible"
98
+ [accessibilityLabel]="folded.accessibilityLabel"
99
+ [accessibilityHint]="folded.accessibilityHint"
100
+ [accessibilityRole]="folded.accessibilityRole"
101
+ [accessibilityState]="folded.accessibilityState"
102
+ [accessibilityValue]="folded.accessibilityValue"
103
+ [accessibilityActions]="folded.accessibilityActions"
104
+ [accessibilityLabelledBy]="folded.accessibilityLabelledBy"
105
+ [importantForAccessibility]="folded.importantForAccessibility"
106
+ [accessibilityLiveRegion]="folded.accessibilityLiveRegion"
107
+ [screenReaderFocusable]="folded.screenReaderFocusable"
108
+ [accessibilityViewIsModal]="folded.accessibilityViewIsModal"
109
+ [accessibilityElementsHidden]="folded.accessibilityElementsHidden"
110
+ [accessibilityIgnoresInvertColors]="folded.accessibilityIgnoresInvertColors"
111
+ [accessibilityLanguage]="folded.accessibilityLanguage"
112
+ [accessibilityRespondsToUserInteraction]="folded.accessibilityRespondsToUserInteraction"
113
+ [accessibilityShowsLargeContentViewer]="folded.accessibilityShowsLargeContentViewer"
114
+ [accessibilityLargeContentTitle]="folded.accessibilityLargeContentTitle"
115
+ (loadStart)="loadStart.emit($event)"
116
+ (load)="load.emit($event)"
117
+ (loadEnd)="loadEnd.emit($event)"
118
+ (error)="error.emit($event)"
119
+ (progress)="progress.emit($event)"
120
+ (partialLoad)="partialLoad.emit($event)"
121
+ (accessibilityAction)="accessibilityAction.emit($event)"
122
+ (accessibilityTap)="accessibilityTap.emit($event)"
123
+ (magicTap)="magicTap.emit($event)"
124
+ (accessibilityEscape)="accessibilityEscape.emit($event)"
125
+ />
126
+ <ng-content></ng-content>
127
+ </symbiote-view>
128
+ `,
129
+ })
130
+ export class ImageBackground implements IAngularImageBackgroundInputs {
131
+ // The inner <Image>'s events as real Angular events, re-emitted verbatim through
132
+ // ImageBackground's own outputs: `(load)="load.emit($event)"`, not `[onLoad]="onLoad"`.
133
+ @Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
134
+ @Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
135
+ @Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
136
+ @Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
137
+ @Output() readonly loadStart = new EventEmitter<ISymbioteEvent>();
138
+ @Output() readonly load = new EventEmitter<ISymbioteEvent>();
139
+ @Output() readonly loadEnd = new EventEmitter<ISymbioteEvent>();
140
+ @Output() readonly error = new EventEmitter<ISymbioteEvent>();
141
+ @Output() readonly progress = new EventEmitter<ISymbioteEvent>();
142
+ @Output() readonly partialLoad = new EventEmitter<ISymbioteEvent>();
143
+ @Input() source?: IImageSourceProp;
144
+ @Input() defaultSource?: IImageSourceProp;
145
+ @Input() loadingIndicatorSource?: IImageSourceProp;
146
+ @Input() style?: IStyleProp<IViewStyle>;
147
+ @Input('imageStyle') imageStyleValue?: IStyleProp<IViewStyle> | string;
148
+ @Input() resizeMode?: IResizeMode;
149
+ @Input() resizeMethod?: IImageProps['resizeMethod'];
150
+ @Input() tintColor?: string;
151
+ @Input() blurRadius?: number;
152
+ @Input() capInsets?: IImageProps['capInsets'];
153
+ @Input() fadeDuration?: number;
154
+ @Input() progressiveRenderingEnabled?: boolean;
155
+ @Input() src?: string;
156
+ @Input() srcSet?: string;
157
+ @Input() alt?: string;
158
+ @Input() width?: number;
159
+ @Input() height?: number;
160
+ @Input() crossOrigin?: 'anonymous' | 'use-credentials';
161
+ @Input() referrerPolicy?: string;
162
+ @Input() testID?: string;
163
+ @Input() nativeID?: string;
164
+ @Input() accessible?: boolean;
165
+ @Input() accessibilityLabel?: string;
166
+ @Input() accessibilityHint?: string;
167
+ @Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
168
+ @Input() accessibilityState?: IAccessibilityStateValue;
169
+ @Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
170
+ @Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
171
+ @Input() accessibilityLabelledBy?: string | string[];
172
+ @Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
173
+ @Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
174
+ @Input() screenReaderFocusable?: boolean;
175
+ @Input() accessibilityViewIsModal?: boolean;
176
+ @Input() accessibilityElementsHidden?: boolean;
177
+ @Input() accessibilityIgnoresInvertColors?: boolean;
178
+ @Input() accessibilityLanguage?: string;
179
+ @Input() accessibilityRespondsToUserInteraction?: boolean;
180
+ @Input() accessibilityShowsLargeContentViewer?: boolean;
181
+ @Input() accessibilityLargeContentTitle?: string;
182
+ @Input() role?: IAriaProps['role'];
183
+ @Input('aria-label') ariaLabel?: string;
184
+ @Input('aria-labelledby') ariaLabelledBy?: string;
185
+ @Input('aria-live') ariaLive?: IAriaProps['aria-live'];
186
+ @Input('aria-hidden') ariaHidden?: boolean;
187
+ @Input('aria-busy') ariaBusy?: boolean;
188
+ @Input('aria-checked') ariaChecked?: boolean | 'mixed';
189
+ @Input('aria-disabled') ariaDisabled?: boolean;
190
+ @Input('aria-expanded') ariaExpanded?: boolean;
191
+ @Input('aria-selected') ariaSelected?: boolean;
192
+ @Input('aria-modal') ariaModal?: boolean;
193
+ @Input('aria-valuemax') ariaValueMax?: number;
194
+ @Input('aria-valuemin') ariaValueMin?: number;
195
+ @Input('aria-valuenow') ariaValueNow?: number;
196
+ @Input('aria-valuetext') ariaValueText?: string;
197
+
198
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
199
+ // onto (see anchorHostStyle's doc comment) — NOT the inner `symbiote-view [style]="wrapperStyle"`
200
+ // one level down. Merged FIRST so the explicit `style` @Input still wins (flattenStyle's
201
+ // later-wins collapse), mirroring every other composed component's anchor merge. `imageStyle`
202
+ // below stays untouched per the ImageBackground-specific nuance: it targets the inner image, not
203
+ // this component's own root.
204
+ private readonly elementRef = inject(ElementRef);
205
+
206
+ get wrapperStyle(): unknown {
207
+ return [anchorHostStyle(this.elementRef), this.style];
208
+ }
209
+
210
+ // renderImageBackground owns the absolute-fill + wrapper-dimension-proxy + imageStyle merge
211
+ // (shared verbatim with React/Vue); read the inner image Descriptor's style off the tree it
212
+ // returns rather than reimplementing it. Only style/imageStyle drive that merge, so the image
213
+ // bag is empty here — the inner <Image> below resolves source / a11y / events on its own.
214
+ get imageStyle(): IStyleProp<IViewStyle> | undefined {
215
+ // imageStyle targets the INNER image (renderImageBackground's own field); resolve a
216
+ // class-name string here, before it flows into the merge.
217
+ const resolvedImageStyleValue =
218
+ typeof this.imageStyleValue === 'string'
219
+ ? resolveClassName(this.imageStyleValue)
220
+ : this.imageStyleValue;
221
+ const inner = renderImageBackground({
222
+ style: this.style,
223
+ imageStyle: resolvedImageStyleValue,
224
+ image: { passthrough: {} },
225
+ }).children[0];
226
+ return typeof inner === 'string'
227
+ ? undefined
228
+ : (inner.props.style as IStyleProp<IViewStyle> | undefined);
229
+ }
230
+
231
+ // Fold the web aria-*/role aliases into the canonical accessibility* props once per render, so the
232
+ // inner image never sees an aria-* key (native ignores them) — the shared transform every adapter
233
+ // runs. The inner <Image> re-runs the same fold idempotently over the already-folded props.
234
+ get folded(): Partial<IAngularImageBackgroundProps> {
235
+ return resolveAccessibilityProps({
236
+ accessibilityLabel: this.accessibilityLabel,
237
+ accessibilityHint: this.accessibilityHint,
238
+ accessibilityRole: this.accessibilityRole,
239
+ accessibilityState: this.accessibilityState,
240
+ accessibilityValue: this.accessibilityValue,
241
+ accessibilityActions: this.accessibilityActions,
242
+ accessibilityLabelledBy: this.accessibilityLabelledBy,
243
+ importantForAccessibility: this.importantForAccessibility,
244
+ accessibilityLiveRegion: this.accessibilityLiveRegion,
245
+ screenReaderFocusable: this.screenReaderFocusable,
246
+ accessibilityViewIsModal: this.accessibilityViewIsModal,
247
+ accessibilityElementsHidden: this.accessibilityElementsHidden,
248
+ accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
249
+ accessibilityLanguage: this.accessibilityLanguage,
250
+ accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
251
+ accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
252
+ accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
253
+ role: this.role,
254
+ 'aria-label': this.ariaLabel,
255
+ 'aria-labelledby': this.ariaLabelledBy,
256
+ 'aria-live': this.ariaLive,
257
+ 'aria-hidden': this.ariaHidden,
258
+ 'aria-busy': this.ariaBusy,
259
+ 'aria-checked': this.ariaChecked,
260
+ 'aria-disabled': this.ariaDisabled,
261
+ 'aria-expanded': this.ariaExpanded,
262
+ 'aria-selected': this.ariaSelected,
263
+ 'aria-modal': this.ariaModal,
264
+ 'aria-valuemax': this.ariaValueMax,
265
+ 'aria-valuemin': this.ariaValueMin,
266
+ 'aria-valuenow': this.ariaValueNow,
267
+ 'aria-valuetext': this.ariaValueText,
268
+ });
269
+ }
270
+ }
@@ -0,0 +1,185 @@
1
+ // InputAccessoryView, the Angular lifecycle half (iOS). RCTInputAccessoryView is a real Fabric
2
+ // host node that docks its content above the keyboard; a TextInput points at it by nativeID through
3
+ // its inputAccessoryViewID. The host-node assembly (nativeID / backgroundColor / style /
4
+ // accessibility forwarding) lives framework-agnostic in
5
+ // @symbiote-native/components/renderInputAccessoryView and is shared verbatim with React/Vue; here
6
+ // Angular supplies only the lifecycle — it folds aria/role, reads the resolved props off the
7
+ // Descriptor, and nests the user children under the host via <ng-content>. The Angular twin of the
8
+ // React/Vue InputAccessoryView. No platform branch, so this stays flat.
9
+
10
+ import {
11
+ CUSTOM_ELEMENTS_SCHEMA,
12
+ ChangeDetectionStrategy,
13
+ Component,
14
+ ElementRef,
15
+ EventEmitter,
16
+ inject,
17
+ Input,
18
+ Output,
19
+ } from '@angular/core';
20
+ import {
21
+ renderInputAccessoryView,
22
+ resolveAccessibilityProps,
23
+ type IAccessibilityProps,
24
+ type IAccessibilityStateValue,
25
+ type IAriaProps,
26
+ } from '@symbiote-native/components';
27
+ import {
28
+ isSymbioteEvent,
29
+ type IStyleProp,
30
+ type ISymbioteEvent,
31
+ type IViewStyle,
32
+ } from '@symbiote-native/engine';
33
+ import {
34
+ anchorHostStyle,
35
+ InputAccessoryViewHost,
36
+ SymbioteHostPropsDirective,
37
+ } from '../../primitives';
38
+
39
+ // Mirrors React's IInputAccessoryViewProps minus children (Angular takes children via
40
+ // <ng-content>), declared per-adapter over the shared a11y base.
41
+ export interface IAngularInputAccessoryViewProps extends IAccessibilityProps, IAriaProps {
42
+ // The id a TextInput's inputAccessoryViewID points at to dock above its keyboard.
43
+ nativeID?: string;
44
+ backgroundColor?: string;
45
+ style?: IStyleProp<IViewStyle>;
46
+ }
47
+
48
+ // What the component itself takes as plain @Input()s: the full surface minus the accessibility
49
+ // callbacks, which it exposes as real @Output() EventEmitters instead (see the class below),
50
+ // mirroring Pressable's IAngularPressableInputs split.
51
+ export type IAngularInputAccessoryViewInputs = Omit<
52
+ IAngularInputAccessoryViewProps,
53
+ 'onAccessibilityAction' | 'onAccessibilityTap' | 'onMagicTap' | 'onAccessibilityEscape'
54
+ >;
55
+
56
+ @Component({
57
+ selector: 'InputAccessoryView',
58
+ standalone: true,
59
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
60
+ imports: [InputAccessoryViewHost, SymbioteHostPropsDirective],
61
+ changeDetection: ChangeDetectionStrategy.OnPush,
62
+ template: `
63
+ <symbiote-input-accessory-view
64
+ [symbioteHostProps]="hostProps"
65
+ (accessibilityAction)="emit(accessibilityAction, $event)"
66
+ (accessibilityTap)="emit(accessibilityTap, $event)"
67
+ (magicTap)="emit(magicTap, $event)"
68
+ (accessibilityEscape)="emit(accessibilityEscape, $event)"
69
+ >
70
+ <ng-content></ng-content>
71
+ </symbiote-input-accessory-view>
72
+ `,
73
+ })
74
+ export class InputAccessoryView implements IAngularInputAccessoryViewInputs {
75
+ @Input() nativeID?: string;
76
+ @Input() backgroundColor?: string;
77
+ @Input() style?: IStyleProp<IViewStyle>;
78
+ @Output() readonly accessibilityAction = new EventEmitter<ISymbioteEvent>();
79
+ @Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
80
+ @Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
81
+ @Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
82
+ @Input() testID?: string;
83
+ @Input() accessible?: boolean;
84
+ @Input() accessibilityLabel?: string;
85
+ @Input() accessibilityHint?: string;
86
+ @Input() accessibilityRole?: IAccessibilityProps['accessibilityRole'];
87
+ @Input() accessibilityState?: IAccessibilityStateValue;
88
+ @Input() accessibilityValue?: IAccessibilityProps['accessibilityValue'];
89
+ @Input() accessibilityActions?: IAccessibilityProps['accessibilityActions'];
90
+ @Input() accessibilityLabelledBy?: string | string[];
91
+ @Input() importantForAccessibility?: IAccessibilityProps['importantForAccessibility'];
92
+ @Input() accessibilityLiveRegion?: IAccessibilityProps['accessibilityLiveRegion'];
93
+ @Input() screenReaderFocusable?: boolean;
94
+ @Input() accessibilityViewIsModal?: boolean;
95
+ @Input() accessibilityElementsHidden?: boolean;
96
+ @Input() accessibilityIgnoresInvertColors?: boolean;
97
+ @Input() accessibilityLanguage?: string;
98
+ @Input() accessibilityRespondsToUserInteraction?: boolean;
99
+ @Input() accessibilityShowsLargeContentViewer?: boolean;
100
+ @Input() accessibilityLargeContentTitle?: string;
101
+ @Input() role?: IAriaProps['role'];
102
+ @Input('aria-label') ariaLabel?: string;
103
+ @Input('aria-labelledby') ariaLabelledBy?: string;
104
+ @Input('aria-live') ariaLive?: IAriaProps['aria-live'];
105
+ @Input('aria-hidden') ariaHidden?: boolean;
106
+ @Input('aria-busy') ariaBusy?: boolean;
107
+ @Input('aria-checked') ariaChecked?: boolean | 'mixed';
108
+ @Input('aria-disabled') ariaDisabled?: boolean;
109
+ @Input('aria-expanded') ariaExpanded?: boolean;
110
+ @Input('aria-selected') ariaSelected?: boolean;
111
+ @Input('aria-modal') ariaModal?: boolean;
112
+ @Input('aria-valuemax') ariaValueMax?: number;
113
+ @Input('aria-valuemin') ariaValueMin?: number;
114
+ @Input('aria-valuenow') ariaValueNow?: number;
115
+ @Input('aria-valuetext') ariaValueText?: string;
116
+
117
+ // This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
118
+ // onto (see anchorHostStyle's doc comment). InputAccessoryView has no inner ViewChild, so this
119
+ // is the only ElementRef in the class.
120
+ private readonly elementRef = inject(ElementRef);
121
+
122
+ // Forward an engine event to the matching @Output(), narrowing the template's untyped $event.
123
+ emit(emitter: EventEmitter<ISymbioteEvent>, event: unknown): void {
124
+ if (isSymbioteEvent(event)) emitter.emit(event);
125
+ }
126
+
127
+ // renderInputAccessoryView owns the host-node assembly (shared with React/Vue); the adapter reads
128
+ // the resolved host props off the Descriptor it returns.
129
+ private get descriptor() {
130
+ return renderInputAccessoryView({
131
+ nativeID: this.nativeID,
132
+ backgroundColor: this.backgroundColor,
133
+ style: this.style,
134
+ passthrough: resolveAccessibilityProps(this.accessibilityInputs()),
135
+ });
136
+ }
137
+
138
+ // The anchor's class-derived style goes FIRST, the Descriptor's resolved style SECOND —
139
+ // flattenStyle's later-wins collapse keeps an explicit [style] winning over its ambient class.
140
+ get hostProps(): Record<string, unknown> {
141
+ const descriptorProps = this.descriptor.props;
142
+ return { ...descriptorProps, style: [anchorHostStyle(this.elementRef), descriptorProps.style] };
143
+ }
144
+
145
+ // Typed as the a11y intersection WITH the string index (the bag renderInputAccessoryView spreads
146
+ // into the host props), so resolveAccessibilityProps's result stays assignable to passthrough.
147
+ private accessibilityInputs(): IAccessibilityProps & IAriaProps & Record<string, unknown> {
148
+ return {
149
+ testID: this.testID,
150
+ accessible: this.accessible,
151
+ accessibilityLabel: this.accessibilityLabel,
152
+ accessibilityHint: this.accessibilityHint,
153
+ accessibilityRole: this.accessibilityRole,
154
+ accessibilityState: this.accessibilityState,
155
+ accessibilityValue: this.accessibilityValue,
156
+ accessibilityActions: this.accessibilityActions,
157
+ accessibilityLabelledBy: this.accessibilityLabelledBy,
158
+ importantForAccessibility: this.importantForAccessibility,
159
+ accessibilityLiveRegion: this.accessibilityLiveRegion,
160
+ screenReaderFocusable: this.screenReaderFocusable,
161
+ accessibilityViewIsModal: this.accessibilityViewIsModal,
162
+ accessibilityElementsHidden: this.accessibilityElementsHidden,
163
+ accessibilityIgnoresInvertColors: this.accessibilityIgnoresInvertColors,
164
+ accessibilityLanguage: this.accessibilityLanguage,
165
+ accessibilityRespondsToUserInteraction: this.accessibilityRespondsToUserInteraction,
166
+ accessibilityShowsLargeContentViewer: this.accessibilityShowsLargeContentViewer,
167
+ accessibilityLargeContentTitle: this.accessibilityLargeContentTitle,
168
+ role: this.role,
169
+ 'aria-label': this.ariaLabel,
170
+ 'aria-labelledby': this.ariaLabelledBy,
171
+ 'aria-live': this.ariaLive,
172
+ 'aria-hidden': this.ariaHidden,
173
+ 'aria-busy': this.ariaBusy,
174
+ 'aria-checked': this.ariaChecked,
175
+ 'aria-disabled': this.ariaDisabled,
176
+ 'aria-expanded': this.ariaExpanded,
177
+ 'aria-selected': this.ariaSelected,
178
+ 'aria-modal': this.ariaModal,
179
+ 'aria-valuemax': this.ariaValueMax,
180
+ 'aria-valuemin': this.ariaValueMin,
181
+ 'aria-valuenow': this.ariaValueNow,
182
+ 'aria-valuetext': this.ariaValueText,
183
+ };
184
+ }
185
+ }
@@ -0,0 +1,43 @@
1
+ import '@angular/compiler';
2
+ import { Component } from '@angular/core';
3
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4
+ import { clearGlobalStyles, registerStyles } from '@symbiote-native/engine';
5
+ import { installFabric } from '@symbiote-native/test-utils';
6
+
7
+ import { mount, unmount } from '../../render';
8
+ import { InputAccessoryView } from './index';
9
+
10
+ const ROOT_TAG = 913;
11
+ const fabric = installFabric();
12
+
13
+ @Component({
14
+ selector: 'symbiote-iav-host',
15
+ standalone: true,
16
+ imports: [InputAccessoryView],
17
+ template: `
18
+ <InputAccessoryView [testID]="'iav'" class="toolbar">
19
+ <symbiote-text>Hello</symbiote-text>
20
+ </InputAccessoryView>
21
+ `,
22
+ })
23
+ class InputAccessoryViewHostFixture {}
24
+
25
+ beforeEach(() => {
26
+ fabric.reset();
27
+ });
28
+ afterEach(() => {
29
+ unmount(ROOT_TAG);
30
+ clearGlobalStyles();
31
+ });
32
+
33
+ describe('InputAccessoryView', () => {
34
+ it('resolves a class= on the InputAccessoryView use site onto the real committed view, not the anchor', async () => {
35
+ registerStyles({ toolbar: { backgroundColor: 'orange' } });
36
+
37
+ mount(ROOT_TAG, InputAccessoryViewHostFixture);
38
+ await new Promise<void>(resolve => setTimeout(resolve, 0));
39
+
40
+ const node = fabric.find(n => n.props.testID === 'iav');
41
+ expect(node?.props.backgroundColor).toBe('orange');
42
+ });
43
+ });