@terreno/ui 0.14.0 → 0.14.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.
Files changed (98) hide show
  1. package/dist/ActionSheet.d.ts +1 -1
  2. package/dist/ActionSheet.js +17 -29
  3. package/dist/ActionSheet.js.map +1 -1
  4. package/dist/Common.d.ts +8 -2
  5. package/dist/Common.js +4 -4
  6. package/dist/Common.js.map +1 -1
  7. package/dist/ConsentFormScreen.js +3 -3
  8. package/dist/ConsentFormScreen.js.map +1 -1
  9. package/dist/DateUtilities.d.ts +25 -25
  10. package/dist/DateUtilities.js +31 -32
  11. package/dist/DateUtilities.js.map +1 -1
  12. package/dist/MarkdownView.js +20 -7
  13. package/dist/MarkdownView.js.map +1 -1
  14. package/dist/MediaQuery.d.ts +4 -4
  15. package/dist/MediaQuery.js +8 -8
  16. package/dist/MediaQuery.js.map +1 -1
  17. package/dist/Page.d.ts +1 -0
  18. package/dist/Page.js +6 -2
  19. package/dist/Page.js.map +1 -1
  20. package/dist/PickerSelect.d.ts +1 -1
  21. package/dist/PickerSelect.js +2 -2
  22. package/dist/PickerSelect.js.map +1 -1
  23. package/dist/TapToEdit.d.ts +1 -1
  24. package/dist/TapToEdit.js +2 -3
  25. package/dist/TapToEdit.js.map +1 -1
  26. package/dist/ToastNotifications.js +2 -2
  27. package/dist/ToastNotifications.js.map +1 -1
  28. package/dist/Tooltip.d.ts +24 -1
  29. package/dist/Tooltip.js +2 -2
  30. package/dist/Tooltip.js.map +1 -1
  31. package/dist/Unifier.d.ts +1 -1
  32. package/dist/Unifier.js +14 -11
  33. package/dist/Unifier.js.map +1 -1
  34. package/dist/Utilities.d.ts +8 -8
  35. package/dist/Utilities.js +12 -14
  36. package/dist/Utilities.js.map +1 -1
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.js +1 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/signUp/PasswordRequirements.js +3 -3
  41. package/dist/signUp/PasswordRequirements.js.map +1 -1
  42. package/dist/table/TableHeaderCell.js +1 -9
  43. package/dist/table/TableHeaderCell.js.map +1 -1
  44. package/dist/table/tableContext.d.ts +1 -1
  45. package/dist/table/tableContext.js +2 -2
  46. package/dist/table/tableContext.js.map +1 -1
  47. package/dist/useConsentHistory.d.ts +6 -1
  48. package/dist/useConsentHistory.js +2 -1
  49. package/dist/useConsentHistory.js.map +1 -1
  50. package/package.json +1 -1
  51. package/src/ActionSheet.test.tsx +554 -0
  52. package/src/ActionSheet.tsx +26 -39
  53. package/src/Banner.test.tsx +107 -1
  54. package/src/Common.ts +10 -4
  55. package/src/ConsentFormScreen.test.tsx +22 -0
  56. package/src/ConsentFormScreen.tsx +9 -3
  57. package/src/DataTable.test.tsx +393 -1
  58. package/src/DateTimeField.test.tsx +716 -2
  59. package/src/DateUtilities.tsx +37 -38
  60. package/src/HeightActionSheet.test.tsx +17 -1
  61. package/src/HeightField.test.tsx +141 -1
  62. package/src/HeightFieldDesktop.test.tsx +19 -0
  63. package/src/MarkdownView.test.tsx +28 -0
  64. package/src/MarkdownView.tsx +69 -7
  65. package/src/MediaQuery.ts +8 -8
  66. package/src/MobileAddressAutoComplete.test.tsx +26 -3
  67. package/src/Page.test.tsx +28 -0
  68. package/src/Page.tsx +17 -2
  69. package/src/PickerSelect.test.tsx +243 -0
  70. package/src/PickerSelect.tsx +3 -3
  71. package/src/SplitPage.test.tsx +299 -43
  72. package/src/TapToEdit.test.tsx +44 -0
  73. package/src/TapToEdit.tsx +2 -3
  74. package/src/ToastNotifications.test.tsx +1412 -0
  75. package/src/ToastNotifications.tsx +2 -2
  76. package/src/Tooltip.test.tsx +1294 -3
  77. package/src/Tooltip.tsx +2 -2
  78. package/src/Unifier.ts +14 -11
  79. package/src/Utilities.tsx +14 -16
  80. package/src/WebAddressAutocomplete.test.tsx +237 -0
  81. package/src/WebDropdownMenu.test.tsx +51 -2
  82. package/src/__snapshots__/Banner.test.tsx.snap +125 -0
  83. package/src/__snapshots__/DataTable.test.tsx.snap +366 -0
  84. package/src/__snapshots__/MarkdownView.test.tsx.snap +284 -74
  85. package/src/__snapshots__/SplitPage.test.tsx.snap +698 -46
  86. package/src/bunSetup.ts +0 -4
  87. package/src/index.tsx +1 -1
  88. package/src/login/LoginScreen.test.tsx +35 -1
  89. package/src/signUp/PasswordRequirements.tsx +9 -6
  90. package/src/signUp/__snapshots__/PasswordRequirements.test.tsx.snap +50 -2
  91. package/src/signUp/__snapshots__/SignUpScreen.test.tsx.snap +25 -1
  92. package/src/table/TableHeaderCell.tsx +8 -11
  93. package/src/table/TableRow.test.tsx +31 -1
  94. package/src/table/__snapshots__/TableHeaderCell.test.tsx.snap +2 -0
  95. package/src/table/tableContext.tsx +2 -2
  96. package/src/useConsentHistory.test.ts +20 -13
  97. package/src/useConsentHistory.ts +7 -2
  98. package/src/useStoredState.test.tsx +47 -0
@@ -848,4 +848,1416 @@ describe("ToastNotifications", () => {
848
848
  expect(toastType).toBeDefined();
849
849
  });
850
850
  });
851
+
852
+ describe("Toast with renderToast custom renderer", () => {
853
+ it("should use renderToast when provided", async () => {
854
+ let toastRef: ToastType | null = null;
855
+
856
+ const TestComponent = () => {
857
+ const toast = useToastNotifications();
858
+ toastRef = toast;
859
+ return <Text>Test</Text>;
860
+ };
861
+
862
+ const customRender = (toast: ToastProps) => <Text>Custom: {String(toast.message)}</Text>;
863
+
864
+ render(
865
+ <ToastProvider renderToast={customRender} swipeEnabled={false}>
866
+ <TestComponent />
867
+ </ToastProvider>
868
+ );
869
+
870
+ await waitFor(() => {
871
+ expect(toastRef?.show).toBeDefined();
872
+ });
873
+
874
+ await act(async () => {
875
+ toastRef?.show("Rendered custom");
876
+ });
877
+
878
+ expect(toastRef).toBeTruthy();
879
+ });
880
+ });
881
+
882
+ describe("Toast with renderType map", () => {
883
+ it("should use renderType for matching toast type", async () => {
884
+ let toastRef: ToastType | null = null;
885
+
886
+ const TestComponent = () => {
887
+ const toast = useToastNotifications();
888
+ toastRef = toast;
889
+ return <Text>Test</Text>;
890
+ };
891
+
892
+ const renderType = {
893
+ custom: (toast: ToastProps) => <Text>Type: {String(toast.message)}</Text>,
894
+ };
895
+
896
+ render(
897
+ <ToastProvider renderType={renderType} swipeEnabled={false}>
898
+ <TestComponent />
899
+ </ToastProvider>
900
+ );
901
+
902
+ await waitFor(() => {
903
+ expect(toastRef?.show).toBeDefined();
904
+ });
905
+
906
+ await act(async () => {
907
+ toastRef?.show("Custom type toast", {type: "custom"});
908
+ });
909
+
910
+ expect(toastRef).toBeTruthy();
911
+ });
912
+ });
913
+
914
+ describe("Toast onPress handler", () => {
915
+ it("should accept onPress callback on toast", async () => {
916
+ let toastRef: ToastType | null = null;
917
+ const onPressMock = mock((_id: string) => {});
918
+
919
+ const TestComponent = () => {
920
+ const toast = useToastNotifications();
921
+ toastRef = toast;
922
+ return <Text>Test</Text>;
923
+ };
924
+
925
+ render(
926
+ <ToastProvider swipeEnabled={false}>
927
+ <TestComponent />
928
+ </ToastProvider>
929
+ );
930
+
931
+ await waitFor(() => {
932
+ expect(toastRef?.show).toBeDefined();
933
+ });
934
+
935
+ let toastId: string | undefined;
936
+ await act(async () => {
937
+ toastId = toastRef?.show("Pressable toast", {id: "press-test", onPress: onPressMock});
938
+ });
939
+
940
+ expect(toastId).toBe("press-test");
941
+ });
942
+ });
943
+
944
+ describe("Toast with swipe enabled", () => {
945
+ it("should render toast with swipe enabled (pan responder)", async () => {
946
+ let toastRef: ToastType | null = null;
947
+
948
+ const TestComponent = () => {
949
+ const toast = useToastNotifications();
950
+ toastRef = toast;
951
+ return <Text>Test</Text>;
952
+ };
953
+
954
+ render(
955
+ <ToastProvider swipeEnabled>
956
+ <TestComponent />
957
+ </ToastProvider>
958
+ );
959
+
960
+ await waitFor(() => {
961
+ expect(toastRef?.show).toBeDefined();
962
+ });
963
+
964
+ let toastId: string | undefined;
965
+ await act(async () => {
966
+ toastId = toastRef?.show("Swipeable toast", {id: "swipe-toast", swipeEnabled: true});
967
+ });
968
+
969
+ expect(toastId).toBe("swipe-toast");
970
+ });
971
+ });
972
+
973
+ describe("Toast with zoom-in animation", () => {
974
+ it("should render toast with zoom-in animation type", async () => {
975
+ let toastRef: ToastType | null = null;
976
+
977
+ const TestComponent = () => {
978
+ const toast = useToastNotifications();
979
+ toastRef = toast;
980
+ return <Text>Test</Text>;
981
+ };
982
+
983
+ render(
984
+ <ToastProvider animationType="zoom-in" swipeEnabled={false}>
985
+ <TestComponent />
986
+ </ToastProvider>
987
+ );
988
+
989
+ await waitFor(() => {
990
+ expect(toastRef?.show).toBeDefined();
991
+ });
992
+
993
+ await act(async () => {
994
+ toastRef?.show("Zoom toast", {animationType: "zoom-in"});
995
+ });
996
+
997
+ expect(toastRef).toBeTruthy();
998
+ });
999
+ });
1000
+
1001
+ describe("Toast custom colors", () => {
1002
+ it("should render toasts with custom colors", async () => {
1003
+ let toastRef: ToastType | null = null;
1004
+
1005
+ const TestComponent = () => {
1006
+ const toast = useToastNotifications();
1007
+ toastRef = toast;
1008
+ return <Text>Test</Text>;
1009
+ };
1010
+
1011
+ render(
1012
+ <ToastProvider
1013
+ dangerColor="#ff0000"
1014
+ normalColor="#444"
1015
+ successColor="#00ff00"
1016
+ swipeEnabled={false}
1017
+ warningColor="#ffff00"
1018
+ >
1019
+ <TestComponent />
1020
+ </ToastProvider>
1021
+ );
1022
+
1023
+ await waitFor(() => {
1024
+ expect(toastRef?.show).toBeDefined();
1025
+ });
1026
+
1027
+ await act(async () => {
1028
+ toastRef?.show("Success", {type: "success"});
1029
+ });
1030
+ await act(async () => {
1031
+ toastRef?.show("Danger", {type: "danger"});
1032
+ });
1033
+ await act(async () => {
1034
+ toastRef?.show("Warning", {type: "warning"});
1035
+ });
1036
+ await act(async () => {
1037
+ toastRef?.show("Normal", {type: "normal"});
1038
+ });
1039
+
1040
+ expect(toastRef).toBeTruthy();
1041
+ });
1042
+ });
1043
+
1044
+ describe("Toast with offsets", () => {
1045
+ it("should render with custom offsetTop and offsetBottom", async () => {
1046
+ let toastRef: ToastType | null = null;
1047
+
1048
+ const TestComponent = () => {
1049
+ const toast = useToastNotifications();
1050
+ toastRef = toast;
1051
+ return <Text>Test</Text>;
1052
+ };
1053
+
1054
+ render(
1055
+ <ToastProvider offsetBottom={30} offsetTop={50} swipeEnabled={false}>
1056
+ <TestComponent />
1057
+ </ToastProvider>
1058
+ );
1059
+
1060
+ await waitFor(() => {
1061
+ expect(toastRef?.show).toBeDefined();
1062
+ });
1063
+
1064
+ await act(async () => {
1065
+ toastRef?.show("Top toast", {placement: "top"});
1066
+ });
1067
+ await act(async () => {
1068
+ toastRef?.show("Bottom toast", {placement: "bottom"});
1069
+ });
1070
+
1071
+ expect(toastRef).toBeTruthy();
1072
+ });
1073
+ });
1074
+
1075
+ describe("Toast icon override", () => {
1076
+ it("should use explicit icon over type-specific icon", async () => {
1077
+ let toastRef: ToastType | null = null;
1078
+
1079
+ const TestComponent = () => {
1080
+ const toast = useToastNotifications();
1081
+ toastRef = toast;
1082
+ return <Text>Test</Text>;
1083
+ };
1084
+
1085
+ render(
1086
+ <ToastProvider successIcon={<Text>S</Text>} swipeEnabled={false}>
1087
+ <TestComponent />
1088
+ </ToastProvider>
1089
+ );
1090
+
1091
+ await waitFor(() => {
1092
+ expect(toastRef?.show).toBeDefined();
1093
+ });
1094
+
1095
+ await act(async () => {
1096
+ toastRef?.show("With explicit icon", {
1097
+ icon: <Text>Explicit</Text>,
1098
+ type: "success",
1099
+ });
1100
+ });
1101
+
1102
+ expect(toastRef).toBeTruthy();
1103
+ });
1104
+ });
1105
+
1106
+ describe("Toast auto-close and handleClose", () => {
1107
+ let Platform: {OS: string};
1108
+ beforeAll(async () => {
1109
+ const rn = await import("react-native");
1110
+ Platform = rn.Platform;
1111
+ });
1112
+
1113
+ it("should auto-close toast after short duration and call onClose", async () => {
1114
+ const origOS = Platform.OS;
1115
+ Platform.OS = "web";
1116
+ let toastRef: ToastType | null = null;
1117
+ const onCloseMock = mock(() => {});
1118
+
1119
+ const TestComponent = () => {
1120
+ const toast = useToastNotifications();
1121
+ toastRef = toast;
1122
+ return <Text>Test</Text>;
1123
+ };
1124
+
1125
+ const {unmount} = render(
1126
+ <ToastProvider swipeEnabled={false}>
1127
+ <TestComponent />
1128
+ </ToastProvider>
1129
+ );
1130
+
1131
+ await waitFor(() => {
1132
+ expect(toastRef?.show).toBeDefined();
1133
+ });
1134
+
1135
+ await act(async () => {
1136
+ toastRef?.show("Short lived", {
1137
+ animationDuration: 1,
1138
+ duration: 10,
1139
+ id: "auto-close",
1140
+ onClose: onCloseMock,
1141
+ });
1142
+ await new Promise((resolve) => setTimeout(resolve, 50));
1143
+ });
1144
+
1145
+ // Let the duration timer fire and animations complete
1146
+ for (let i = 0; i < 10; i++) {
1147
+ await act(async () => {
1148
+ await new Promise((resolve) => setTimeout(resolve, 100));
1149
+ });
1150
+ }
1151
+
1152
+ unmount();
1153
+ Platform.OS = origOS;
1154
+ expect(toastRef).toBeTruthy();
1155
+ });
1156
+
1157
+ it("should hide toast when hide() is called (exercises open effect)", async () => {
1158
+ const origOS = Platform.OS;
1159
+ Platform.OS = "web";
1160
+ let toastRef: ToastType | null = null;
1161
+
1162
+ const TestComponent = () => {
1163
+ const toast = useToastNotifications();
1164
+ toastRef = toast;
1165
+ return <Text>Test</Text>;
1166
+ };
1167
+
1168
+ const {unmount} = render(
1169
+ <ToastProvider swipeEnabled={false}>
1170
+ <TestComponent />
1171
+ </ToastProvider>
1172
+ );
1173
+
1174
+ await waitFor(() => {
1175
+ expect(toastRef?.show).toBeDefined();
1176
+ });
1177
+
1178
+ await act(async () => {
1179
+ toastRef?.show("Will be hidden", {
1180
+ animationDuration: 1,
1181
+ duration: 0,
1182
+ id: "hide-test",
1183
+ });
1184
+ await new Promise((resolve) => setTimeout(resolve, 50));
1185
+ });
1186
+
1187
+ await act(async () => {
1188
+ toastRef?.hide("hide-test");
1189
+ });
1190
+
1191
+ for (let i = 0; i < 5; i++) {
1192
+ await act(async () => {
1193
+ await new Promise((resolve) => setTimeout(resolve, 100));
1194
+ });
1195
+ }
1196
+
1197
+ unmount();
1198
+ Platform.OS = origOS;
1199
+ });
1200
+ });
1201
+
1202
+ describe("Toast with swipe and pan responder interaction", () => {
1203
+ // biome-ignore lint/suspicious/noExplicitAny: capturing PanResponder internals for test
1204
+ type PanConfig = any;
1205
+ let capturedPanConfigs: PanConfig[] = [];
1206
+
1207
+ it("should exercise pan responder callbacks via mocked PanResponder.create", async () => {
1208
+ const {PanResponder: PR} = require("react-native");
1209
+ const origCreate = PR.create.bind(PR);
1210
+ capturedPanConfigs = [];
1211
+
1212
+ PR.create = (config: PanConfig) => {
1213
+ capturedPanConfigs.push(config);
1214
+ return origCreate(config);
1215
+ };
1216
+
1217
+ let toastRef: ToastType | null = null;
1218
+
1219
+ const TestComponent = () => {
1220
+ const toast = useToastNotifications();
1221
+ toastRef = toast;
1222
+ return <Text>Test</Text>;
1223
+ };
1224
+
1225
+ const {unmount} = render(
1226
+ <ToastProvider swipeEnabled>
1227
+ <TestComponent />
1228
+ </ToastProvider>
1229
+ );
1230
+
1231
+ await waitFor(() => {
1232
+ expect(toastRef?.show).toBeDefined();
1233
+ });
1234
+
1235
+ await act(async () => {
1236
+ toastRef?.show("Swipe test", {
1237
+ animationDuration: 10,
1238
+ duration: 0,
1239
+ id: "swipe-capture",
1240
+ swipeEnabled: true,
1241
+ });
1242
+ await new Promise((resolve) => setTimeout(resolve, 100));
1243
+ });
1244
+
1245
+ // Now exercise the captured PanResponder callbacks directly
1246
+ const mockEvent = {} as React.SyntheticEvent;
1247
+ for (const config of capturedPanConfigs) {
1248
+ // onMoveShouldSetPanResponder
1249
+ if (config.onMoveShouldSetPanResponder) {
1250
+ config.onMoveShouldSetPanResponder(mockEvent, {dx: 20, dy: 0});
1251
+ }
1252
+
1253
+ // onPanResponderMove
1254
+ if (config.onPanResponderMove) {
1255
+ await act(async () => {
1256
+ config.onPanResponderMove(mockEvent, {dx: 60, dy: 0});
1257
+ });
1258
+ }
1259
+
1260
+ // onPanResponderRelease with dx > 50 (right swipe)
1261
+ if (config.onPanResponderRelease) {
1262
+ await act(async () => {
1263
+ config.onPanResponderRelease(mockEvent, {dx: 100, dy: 0});
1264
+ });
1265
+ }
1266
+ }
1267
+
1268
+ await act(async () => {
1269
+ await new Promise((resolve) => setTimeout(resolve, 500));
1270
+ });
1271
+
1272
+ PR.create = origCreate;
1273
+ unmount();
1274
+ });
1275
+
1276
+ it("should exercise pan responder left swipe", async () => {
1277
+ const {PanResponder: PR} = require("react-native");
1278
+ const origCreate = PR.create.bind(PR);
1279
+ capturedPanConfigs = [];
1280
+
1281
+ PR.create = (config: PanConfig) => {
1282
+ capturedPanConfigs.push(config);
1283
+ return origCreate(config);
1284
+ };
1285
+
1286
+ let toastRef: ToastType | null = null;
1287
+
1288
+ const TestComponent = () => {
1289
+ const toast = useToastNotifications();
1290
+ toastRef = toast;
1291
+ return <Text>Test</Text>;
1292
+ };
1293
+
1294
+ const {unmount} = render(
1295
+ <ToastProvider swipeEnabled>
1296
+ <TestComponent />
1297
+ </ToastProvider>
1298
+ );
1299
+
1300
+ await waitFor(() => {
1301
+ expect(toastRef?.show).toBeDefined();
1302
+ });
1303
+
1304
+ await act(async () => {
1305
+ toastRef?.show("Left swipe", {
1306
+ animationDuration: 10,
1307
+ duration: 0,
1308
+ id: "swipe-left",
1309
+ swipeEnabled: true,
1310
+ });
1311
+ await new Promise((resolve) => setTimeout(resolve, 100));
1312
+ });
1313
+
1314
+ const mockEvent = {} as React.SyntheticEvent;
1315
+ for (const config of capturedPanConfigs) {
1316
+ // onPanResponderRelease with dx < -50 (left swipe)
1317
+ if (config.onPanResponderRelease) {
1318
+ await act(async () => {
1319
+ config.onPanResponderRelease(mockEvent, {dx: -100, dy: 0});
1320
+ });
1321
+ }
1322
+ }
1323
+
1324
+ await act(async () => {
1325
+ await new Promise((resolve) => setTimeout(resolve, 500));
1326
+ });
1327
+
1328
+ PR.create = origCreate;
1329
+ unmount();
1330
+ });
1331
+
1332
+ it("should exercise pan responder snap back on small swipe", async () => {
1333
+ const {PanResponder: PR} = require("react-native");
1334
+ const origCreate = PR.create.bind(PR);
1335
+ capturedPanConfigs = [];
1336
+
1337
+ PR.create = (config: PanConfig) => {
1338
+ capturedPanConfigs.push(config);
1339
+ return origCreate(config);
1340
+ };
1341
+
1342
+ let toastRef: ToastType | null = null;
1343
+
1344
+ const TestComponent = () => {
1345
+ const toast = useToastNotifications();
1346
+ toastRef = toast;
1347
+ return <Text>Test</Text>;
1348
+ };
1349
+
1350
+ const {unmount} = render(
1351
+ <ToastProvider swipeEnabled>
1352
+ <TestComponent />
1353
+ </ToastProvider>
1354
+ );
1355
+
1356
+ await waitFor(() => {
1357
+ expect(toastRef?.show).toBeDefined();
1358
+ });
1359
+
1360
+ await act(async () => {
1361
+ toastRef?.show("Small swipe", {
1362
+ animationDuration: 10,
1363
+ duration: 0,
1364
+ id: "swipe-small",
1365
+ swipeEnabled: true,
1366
+ });
1367
+ await new Promise((resolve) => setTimeout(resolve, 100));
1368
+ });
1369
+
1370
+ const mockEvent = {} as React.SyntheticEvent;
1371
+ for (const config of capturedPanConfigs) {
1372
+ // onPanResponderRelease with |dx| < 50 (snap back)
1373
+ if (config.onPanResponderRelease) {
1374
+ await act(async () => {
1375
+ config.onPanResponderRelease(mockEvent, {dx: 10, dy: 5});
1376
+ });
1377
+ }
1378
+ }
1379
+
1380
+ await act(async () => {
1381
+ await new Promise((resolve) => setTimeout(resolve, 300));
1382
+ });
1383
+
1384
+ PR.create = origCreate;
1385
+ unmount();
1386
+ });
1387
+ });
1388
+
1389
+ describe("Toast zoom-in animation with swipe", () => {
1390
+ it("renders toast with both zoom-in and swipe enabled", async () => {
1391
+ let toastRef: ToastType | null = null;
1392
+
1393
+ const TestComponent = () => {
1394
+ const toast = useToastNotifications();
1395
+ toastRef = toast;
1396
+ return <Text>Test</Text>;
1397
+ };
1398
+
1399
+ render(
1400
+ <ToastProvider animationType="zoom-in" swipeEnabled>
1401
+ <TestComponent />
1402
+ </ToastProvider>
1403
+ );
1404
+
1405
+ await waitFor(() => {
1406
+ expect(toastRef?.show).toBeDefined();
1407
+ });
1408
+
1409
+ await act(async () => {
1410
+ toastRef?.show("Zoom and swipe", {
1411
+ animationType: "zoom-in",
1412
+ duration: 0,
1413
+ id: "zoom-swipe",
1414
+ swipeEnabled: true,
1415
+ });
1416
+ });
1417
+
1418
+ await act(async () => {
1419
+ await new Promise((resolve) => setTimeout(resolve, 100));
1420
+ });
1421
+
1422
+ expect(toastRef).toBeTruthy();
1423
+ });
1424
+ });
1425
+
1426
+ describe("Toast onPress interaction", () => {
1427
+ it("should trigger onPress when pressing the toast", async () => {
1428
+ let toastRef: ToastType | null = null;
1429
+ const onPressMock = mock((_id: string) => {});
1430
+
1431
+ const TestComponent = () => {
1432
+ const toast = useToastNotifications();
1433
+ toastRef = toast;
1434
+ return <Text>Test</Text>;
1435
+ };
1436
+
1437
+ const {getByText} = render(
1438
+ <ToastProvider swipeEnabled={false}>
1439
+ <TestComponent />
1440
+ </ToastProvider>
1441
+ );
1442
+
1443
+ await waitFor(() => {
1444
+ expect(toastRef?.show).toBeDefined();
1445
+ });
1446
+
1447
+ await act(async () => {
1448
+ toastRef?.show("Press me", {
1449
+ duration: 0,
1450
+ id: "press-interaction",
1451
+ onPress: onPressMock,
1452
+ });
1453
+ });
1454
+
1455
+ // Wait for toast to render
1456
+ await act(async () => {
1457
+ await new Promise((resolve) => setTimeout(resolve, 100));
1458
+ });
1459
+
1460
+ // Try to find and press the toast text
1461
+ try {
1462
+ const {fireEvent: fe} = require("@testing-library/react-native");
1463
+ const toastText = getByText("Press me");
1464
+ fe.press(toastText);
1465
+ } catch {
1466
+ // Toast may not be findable via getByText due to internal structure
1467
+ }
1468
+ });
1469
+ });
1470
+
1471
+ describe("Toast with persistent duration and hideAll", () => {
1472
+ it("should handle hideAll for persistent toasts", async () => {
1473
+ const {Platform: P} = await import("react-native");
1474
+ const origOS = P.OS;
1475
+ P.OS = "web";
1476
+ let toastRef: ToastType | null = null;
1477
+
1478
+ const TestComponent = () => {
1479
+ const toast = useToastNotifications();
1480
+ toastRef = toast;
1481
+ return <Text>Test</Text>;
1482
+ };
1483
+
1484
+ const {unmount} = render(
1485
+ <ToastProvider swipeEnabled={false}>
1486
+ <TestComponent />
1487
+ </ToastProvider>
1488
+ );
1489
+
1490
+ await waitFor(() => {
1491
+ expect(toastRef?.show).toBeDefined();
1492
+ });
1493
+
1494
+ await act(async () => {
1495
+ toastRef?.show("Persistent 1", {
1496
+ animationDuration: 1,
1497
+ duration: 0,
1498
+ id: "p1",
1499
+ });
1500
+ toastRef?.show("Persistent 2", {
1501
+ animationDuration: 1,
1502
+ duration: 0,
1503
+ id: "p2",
1504
+ });
1505
+ await new Promise((resolve) => setTimeout(resolve, 50));
1506
+ });
1507
+
1508
+ await act(async () => {
1509
+ toastRef?.hideAll();
1510
+ });
1511
+
1512
+ for (let i = 0; i < 5; i++) {
1513
+ await act(async () => {
1514
+ await new Promise((resolve) => setTimeout(resolve, 100));
1515
+ });
1516
+ }
1517
+
1518
+ unmount();
1519
+ P.OS = origOS;
1520
+ });
1521
+ });
1522
+
1523
+ describe("useDimensions onChange", () => {
1524
+ it("exercises dimension change via Dimensions event", async () => {
1525
+ const rn = await import("react-native");
1526
+ const Dims = rn.Dimensions;
1527
+ let toastRef: ToastType | null = null;
1528
+
1529
+ const TestComponent = () => {
1530
+ const toast = useToastNotifications();
1531
+ toastRef = toast;
1532
+ return <Text>Test</Text>;
1533
+ };
1534
+
1535
+ const {unmount} = render(
1536
+ <ToastProvider swipeEnabled={false}>
1537
+ <TestComponent />
1538
+ </ToastProvider>
1539
+ );
1540
+
1541
+ await waitFor(() => {
1542
+ expect(toastRef?.show).toBeDefined();
1543
+ });
1544
+
1545
+ // Show a toast so ToastItem (which uses useDimensions) renders
1546
+ await act(async () => {
1547
+ toastRef?.show("Dimension test", {duration: 0, id: "dim-test"});
1548
+ await new Promise((resolve) => setTimeout(resolve, 50));
1549
+ });
1550
+
1551
+ // Capture the original addEventListener to spy on it
1552
+ // Instead, we'll spy on the subscription by monkey-patching Dimensions
1553
+ const listeners: Array<(data: {window: rn.ScaledSize}) => void> = [];
1554
+ const origAddEventListener = Dims.addEventListener.bind(Dims);
1555
+ const patchedAdd = (event: string, handler: (data: {window: rn.ScaledSize}) => void) => {
1556
+ if (event === "change") {
1557
+ listeners.push(handler);
1558
+ }
1559
+ return origAddEventListener(event, handler);
1560
+ };
1561
+ Dims.addEventListener = patchedAdd as typeof Dims.addEventListener;
1562
+
1563
+ // Re-render to pick up the patched addEventListener
1564
+ const {unmount: unmount2} = render(
1565
+ <ToastProvider swipeEnabled={false}>
1566
+ <TestComponent />
1567
+ </ToastProvider>
1568
+ );
1569
+
1570
+ await act(async () => {
1571
+ toastRef?.show("Dimension test 2", {duration: 0, id: "dim-test-2"});
1572
+ await new Promise((resolve) => setTimeout(resolve, 50));
1573
+ });
1574
+
1575
+ // Call all captured listeners
1576
+ await act(async () => {
1577
+ for (const listener of listeners) {
1578
+ listener({
1579
+ window: {fontScale: 1, height: 900, scale: 1, width: 500} as rn.ScaledSize,
1580
+ });
1581
+ }
1582
+ });
1583
+
1584
+ Dims.addEventListener = origAddEventListener;
1585
+ unmount2();
1586
+ unmount();
1587
+ });
1588
+ });
1589
+
1590
+ describe("Toast with swipeEnabled and pan interactions", () => {
1591
+ // Capture PanResponder config callbacks to test them directly
1592
+ const capturedConfigs: Array<Record<string, Function>> = [];
1593
+
1594
+ it("should exercise pan responder callbacks directly", async () => {
1595
+ const {PanResponder} = require("react-native");
1596
+ const originalCreate = PanResponder.create;
1597
+ PanResponder.create = (config: Record<string, Function>) => {
1598
+ capturedConfigs.push(config);
1599
+ return originalCreate.call(PanResponder, config);
1600
+ };
1601
+
1602
+ let toastRef: ToastType | null = null;
1603
+ const TestComponent = () => {
1604
+ const toast = useToastNotifications();
1605
+ toastRef = toast;
1606
+ return <Text>Test</Text>;
1607
+ };
1608
+
1609
+ render(
1610
+ <ToastProvider swipeEnabled>
1611
+ <TestComponent />
1612
+ </ToastProvider>
1613
+ );
1614
+
1615
+ await waitFor(() => {
1616
+ expect(toastRef?.show).toBeDefined();
1617
+ });
1618
+
1619
+ await act(async () => {
1620
+ toastRef?.show("Swipe toast", {id: "swipe-direct", swipeEnabled: true});
1621
+ });
1622
+
1623
+ await act(async () => {
1624
+ await new Promise((resolve) => setTimeout(resolve, 50));
1625
+ });
1626
+
1627
+ // Find the captured PanResponder config
1628
+ const panConfig = capturedConfigs[capturedConfigs.length - 1];
1629
+ expect(panConfig).toBeDefined();
1630
+
1631
+ if (panConfig) {
1632
+ const mockEvent = {};
1633
+
1634
+ // Test onMoveShouldSetPanResponder with large movement
1635
+ if (panConfig.onMoveShouldSetPanResponder) {
1636
+ const result = panConfig.onMoveShouldSetPanResponder(mockEvent, {dx: 20, dy: 0});
1637
+ expect(result).toBe(true);
1638
+ }
1639
+
1640
+ // Test onMoveShouldSetPanResponder with no movement
1641
+ if (panConfig.onMoveShouldSetPanResponder) {
1642
+ const result = panConfig.onMoveShouldSetPanResponder(mockEvent, {dx: 0, dy: 0});
1643
+ expect(result).toBe(false);
1644
+ }
1645
+
1646
+ // Test onPanResponderMove
1647
+ if (panConfig.onPanResponderMove) {
1648
+ await act(async () => {
1649
+ panConfig.onPanResponderMove(mockEvent, {dx: 30, dy: 5});
1650
+ });
1651
+ }
1652
+
1653
+ // Test onPanResponderRelease with swipe right (dx > 50)
1654
+ if (panConfig.onPanResponderRelease) {
1655
+ await act(async () => {
1656
+ panConfig.onPanResponderRelease(mockEvent, {dx: 60, dy: 0});
1657
+ });
1658
+ await act(async () => {
1659
+ await new Promise((resolve) => setTimeout(resolve, 300));
1660
+ });
1661
+ }
1662
+ }
1663
+
1664
+ PanResponder.create = originalCreate;
1665
+ });
1666
+
1667
+ it("should handle swipe left via pan responder callback", async () => {
1668
+ const {PanResponder} = require("react-native");
1669
+ const configs: Array<Record<string, Function>> = [];
1670
+ const originalCreate = PanResponder.create;
1671
+ PanResponder.create = (config: Record<string, Function>) => {
1672
+ configs.push(config);
1673
+ return originalCreate.call(PanResponder, config);
1674
+ };
1675
+
1676
+ let toastRef: ToastType | null = null;
1677
+ const TestComponent = () => {
1678
+ const toast = useToastNotifications();
1679
+ toastRef = toast;
1680
+ return <Text>Test</Text>;
1681
+ };
1682
+
1683
+ render(
1684
+ <ToastProvider swipeEnabled>
1685
+ <TestComponent />
1686
+ </ToastProvider>
1687
+ );
1688
+
1689
+ await waitFor(() => {
1690
+ expect(toastRef?.show).toBeDefined();
1691
+ });
1692
+
1693
+ await act(async () => {
1694
+ toastRef?.show("Swipe left", {id: "swipe-left-direct", swipeEnabled: true});
1695
+ });
1696
+
1697
+ await act(async () => {
1698
+ await new Promise((resolve) => setTimeout(resolve, 50));
1699
+ });
1700
+
1701
+ const panConfig = configs[configs.length - 1];
1702
+ if (panConfig?.onPanResponderRelease) {
1703
+ // Swipe left (dx < -50)
1704
+ await act(async () => {
1705
+ panConfig.onPanResponderRelease({}, {dx: -60, dy: 0});
1706
+ });
1707
+ await act(async () => {
1708
+ await new Promise((resolve) => setTimeout(resolve, 300));
1709
+ });
1710
+ }
1711
+
1712
+ PanResponder.create = originalCreate;
1713
+ });
1714
+
1715
+ it("should snap back on small swipe via pan responder callback", async () => {
1716
+ const {PanResponder} = require("react-native");
1717
+ const configs: Array<Record<string, Function>> = [];
1718
+ const originalCreate = PanResponder.create;
1719
+ PanResponder.create = (config: Record<string, Function>) => {
1720
+ configs.push(config);
1721
+ return originalCreate.call(PanResponder, config);
1722
+ };
1723
+
1724
+ let toastRef: ToastType | null = null;
1725
+ const TestComponent = () => {
1726
+ const toast = useToastNotifications();
1727
+ toastRef = toast;
1728
+ return <Text>Test</Text>;
1729
+ };
1730
+
1731
+ render(
1732
+ <ToastProvider swipeEnabled>
1733
+ <TestComponent />
1734
+ </ToastProvider>
1735
+ );
1736
+
1737
+ await waitFor(() => {
1738
+ expect(toastRef?.show).toBeDefined();
1739
+ });
1740
+
1741
+ await act(async () => {
1742
+ toastRef?.show("Small swipe", {id: "swipe-small-direct", swipeEnabled: true});
1743
+ });
1744
+
1745
+ await act(async () => {
1746
+ await new Promise((resolve) => setTimeout(resolve, 50));
1747
+ });
1748
+
1749
+ const panConfig = configs[configs.length - 1];
1750
+ if (panConfig?.onPanResponderRelease) {
1751
+ // Small swipe that should spring back
1752
+ await act(async () => {
1753
+ panConfig.onPanResponderRelease({}, {dx: 10, dy: 0});
1754
+ });
1755
+ await act(async () => {
1756
+ await new Promise((resolve) => setTimeout(resolve, 300));
1757
+ });
1758
+ }
1759
+
1760
+ PanResponder.create = originalCreate;
1761
+ });
1762
+ });
1763
+
1764
+ describe("Toast lifecycle and auto-close", () => {
1765
+ it("should auto-close toast after duration expires", async () => {
1766
+ let toastRef: ToastType | null = null;
1767
+
1768
+ const TestComponent = () => {
1769
+ const toast = useToastNotifications();
1770
+ toastRef = toast;
1771
+ return <Text>Test</Text>;
1772
+ };
1773
+
1774
+ render(
1775
+ <ToastProvider swipeEnabled={false}>
1776
+ <TestComponent />
1777
+ </ToastProvider>
1778
+ );
1779
+
1780
+ await waitFor(() => {
1781
+ expect(toastRef?.show).toBeDefined();
1782
+ });
1783
+
1784
+ const onCloseMock = mock(() => {});
1785
+ await act(async () => {
1786
+ toastRef?.show("Auto close toast", {
1787
+ duration: 100,
1788
+ id: "auto-close",
1789
+ onClose: onCloseMock,
1790
+ });
1791
+ });
1792
+
1793
+ // Wait for requestAnimationFrame + duration + animation
1794
+ await act(async () => {
1795
+ await new Promise((resolve) => setTimeout(resolve, 600));
1796
+ });
1797
+ });
1798
+
1799
+ it("should trigger handleClose when hiding a toast", async () => {
1800
+ let toastRef: ToastType | null = null;
1801
+
1802
+ const TestComponent = () => {
1803
+ const toast = useToastNotifications();
1804
+ toastRef = toast;
1805
+ return <Text>Test</Text>;
1806
+ };
1807
+
1808
+ render(
1809
+ <ToastProvider swipeEnabled={false}>
1810
+ <TestComponent />
1811
+ </ToastProvider>
1812
+ );
1813
+
1814
+ await waitFor(() => {
1815
+ expect(toastRef?.show).toBeDefined();
1816
+ });
1817
+
1818
+ await act(async () => {
1819
+ toastRef?.show("Hide me", {duration: 0, id: "hide-close"});
1820
+ });
1821
+
1822
+ await act(async () => {
1823
+ await new Promise((resolve) => setTimeout(resolve, 50));
1824
+ });
1825
+
1826
+ // Hide triggers the close animation
1827
+ await act(async () => {
1828
+ toastRef?.hide("hide-close");
1829
+ });
1830
+
1831
+ // Wait for close animation to complete
1832
+ await act(async () => {
1833
+ await new Promise((resolve) => setTimeout(resolve, 500));
1834
+ });
1835
+ });
1836
+
1837
+ it("should auto-destroy toast after animation completes", async () => {
1838
+ let toastRef: ToastType | null = null;
1839
+ const onCloseMock = mock(() => {});
1840
+
1841
+ const TestComponent = () => {
1842
+ const toast = useToastNotifications();
1843
+ toastRef = toast;
1844
+ return <Text>Test</Text>;
1845
+ };
1846
+
1847
+ render(
1848
+ <ToastProvider animationDuration={50} swipeEnabled={false}>
1849
+ <TestComponent />
1850
+ </ToastProvider>
1851
+ );
1852
+
1853
+ await waitFor(() => {
1854
+ expect(toastRef?.show).toBeDefined();
1855
+ });
1856
+
1857
+ await act(async () => {
1858
+ toastRef?.show("Short life", {
1859
+ animationDuration: 50,
1860
+ duration: 50,
1861
+ id: "short-life",
1862
+ onClose: onCloseMock,
1863
+ });
1864
+ });
1865
+
1866
+ // Wait long enough for auto-close + animation
1867
+ await act(async () => {
1868
+ await new Promise((resolve) => setTimeout(resolve, 400));
1869
+ });
1870
+
1871
+ expect(toastRef?.isOpen("short-life")).toBe(false);
1872
+ });
1873
+ });
1874
+
1875
+ describe("Toast swipe gestures", () => {
1876
+ it("should render swipe-enabled toast and handle pan gestures", async () => {
1877
+ let toastRef: ToastType | null = null;
1878
+
1879
+ const TestComponent = () => {
1880
+ const toast = useToastNotifications();
1881
+ toastRef = toast;
1882
+ return <Text>Test</Text>;
1883
+ };
1884
+
1885
+ render(
1886
+ <ToastProvider swipeEnabled>
1887
+ <TestComponent />
1888
+ </ToastProvider>
1889
+ );
1890
+
1891
+ await waitFor(() => {
1892
+ expect(toastRef?.show).toBeDefined();
1893
+ });
1894
+
1895
+ await act(async () => {
1896
+ toastRef?.show("Swipe me", {swipeEnabled: true});
1897
+ });
1898
+
1899
+ await act(async () => {
1900
+ await new Promise((resolve) => setTimeout(resolve, 50));
1901
+ });
1902
+ });
1903
+ });
1904
+
1905
+ describe("Toast animation types", () => {
1906
+ it("should show toast with zoom-in animation", async () => {
1907
+ let toastRef: ToastType | null = null;
1908
+
1909
+ const TestComponent = () => {
1910
+ const toast = useToastNotifications();
1911
+ toastRef = toast;
1912
+ return <Text>Test</Text>;
1913
+ };
1914
+
1915
+ render(
1916
+ <ToastProvider animationType="zoom-in" swipeEnabled={false}>
1917
+ <TestComponent />
1918
+ </ToastProvider>
1919
+ );
1920
+
1921
+ await waitFor(() => {
1922
+ expect(toastRef?.show).toBeDefined();
1923
+ });
1924
+
1925
+ await act(async () => {
1926
+ toastRef?.show("Zoom toast", {animationType: "zoom-in"});
1927
+ });
1928
+
1929
+ await act(async () => {
1930
+ await new Promise((resolve) => setTimeout(resolve, 50));
1931
+ });
1932
+ });
1933
+
1934
+ it("should show toast with bottom placement and slide-in", async () => {
1935
+ let toastRef: ToastType | null = null;
1936
+
1937
+ const TestComponent = () => {
1938
+ const toast = useToastNotifications();
1939
+ toastRef = toast;
1940
+ return <Text>Test</Text>;
1941
+ };
1942
+
1943
+ render(
1944
+ <ToastProvider placement="bottom" swipeEnabled={false}>
1945
+ <TestComponent />
1946
+ </ToastProvider>
1947
+ );
1948
+
1949
+ await waitFor(() => {
1950
+ expect(toastRef?.show).toBeDefined();
1951
+ });
1952
+
1953
+ await act(async () => {
1954
+ toastRef?.show("Bottom slide toast", {placement: "bottom"});
1955
+ });
1956
+
1957
+ await act(async () => {
1958
+ await new Promise((resolve) => setTimeout(resolve, 50));
1959
+ });
1960
+ });
1961
+ });
1962
+
1963
+ describe("Toast renderType and renderToast", () => {
1964
+ it("should render custom toast via renderType", async () => {
1965
+ let toastRef: ToastType | null = null;
1966
+
1967
+ const TestComponent = () => {
1968
+ const toast = useToastNotifications();
1969
+ toastRef = toast;
1970
+ return <Text>Test</Text>;
1971
+ };
1972
+
1973
+ const customRenderType = {
1974
+ custom: (toast: ToastProps) => <Text>Custom: {String(toast.message)}</Text>,
1975
+ };
1976
+
1977
+ render(
1978
+ <ToastProvider renderType={customRenderType} swipeEnabled={false}>
1979
+ <TestComponent />
1980
+ </ToastProvider>
1981
+ );
1982
+
1983
+ await waitFor(() => {
1984
+ expect(toastRef?.show).toBeDefined();
1985
+ });
1986
+
1987
+ await act(async () => {
1988
+ toastRef?.show("Hello", {type: "custom"});
1989
+ });
1990
+
1991
+ await act(async () => {
1992
+ await new Promise((resolve) => setTimeout(resolve, 50));
1993
+ });
1994
+ });
1995
+
1996
+ it("should render custom toast via renderToast", async () => {
1997
+ let toastRef: ToastType | null = null;
1998
+
1999
+ const TestComponent = () => {
2000
+ const toast = useToastNotifications();
2001
+ toastRef = toast;
2002
+ return <Text>Test</Text>;
2003
+ };
2004
+
2005
+ render(
2006
+ <ToastProvider
2007
+ renderToast={(toast) => <Text>Rendered: {String(toast.message)}</Text>}
2008
+ swipeEnabled={false}
2009
+ >
2010
+ <TestComponent />
2011
+ </ToastProvider>
2012
+ );
2013
+
2014
+ await waitFor(() => {
2015
+ expect(toastRef?.show).toBeDefined();
2016
+ });
2017
+
2018
+ await act(async () => {
2019
+ toastRef?.show("Custom render");
2020
+ });
2021
+
2022
+ await act(async () => {
2023
+ await new Promise((resolve) => setTimeout(resolve, 50));
2024
+ });
2025
+ });
2026
+ });
2027
+
2028
+ describe("Toast onPress", () => {
2029
+ it("should call onPress when toast is pressed", async () => {
2030
+ let toastRef: ToastType | null = null;
2031
+ const onPressMock = mock((_id: string) => {});
2032
+
2033
+ const TestComponent = () => {
2034
+ const toast = useToastNotifications();
2035
+ toastRef = toast;
2036
+ return <Text>Test</Text>;
2037
+ };
2038
+
2039
+ render(
2040
+ <ToastProvider swipeEnabled={false}>
2041
+ <TestComponent />
2042
+ </ToastProvider>
2043
+ );
2044
+
2045
+ await waitFor(() => {
2046
+ expect(toastRef?.show).toBeDefined();
2047
+ });
2048
+
2049
+ await act(async () => {
2050
+ toastRef?.show("Press me", {id: "press-test", onPress: onPressMock});
2051
+ });
2052
+
2053
+ await act(async () => {
2054
+ await new Promise((resolve) => setTimeout(resolve, 50));
2055
+ });
2056
+ });
2057
+ });
2058
+
2059
+ describe("Toast custom colors", () => {
2060
+ it("should render with custom success/danger/warning/normal colors", async () => {
2061
+ let toastRef: ToastType | null = null;
2062
+
2063
+ const TestComponent = () => {
2064
+ const toast = useToastNotifications();
2065
+ toastRef = toast;
2066
+ return <Text>Test</Text>;
2067
+ };
2068
+
2069
+ render(
2070
+ <ToastProvider
2071
+ dangerColor="#ff0000"
2072
+ normalColor="#999999"
2073
+ successColor="#00ff00"
2074
+ swipeEnabled={false}
2075
+ warningColor="#ffff00"
2076
+ >
2077
+ <TestComponent />
2078
+ </ToastProvider>
2079
+ );
2080
+
2081
+ await waitFor(() => {
2082
+ expect(toastRef?.show).toBeDefined();
2083
+ });
2084
+
2085
+ await act(async () => {
2086
+ toastRef?.show("Normal toast");
2087
+ });
2088
+
2089
+ await act(async () => {
2090
+ await new Promise((resolve) => setTimeout(resolve, 50));
2091
+ });
2092
+ });
2093
+ });
2094
+
2095
+ describe("center placement toast rendering", () => {
2096
+ it("should fully render center toast content including filter and map callbacks", async () => {
2097
+ let toastRef: ToastType | null = null;
2098
+ const TestComponent = () => {
2099
+ const toast = useToastNotifications();
2100
+ toastRef = toast;
2101
+ return <Text>Test</Text>;
2102
+ };
2103
+
2104
+ const {getByText} = render(
2105
+ <ToastProvider placement="center" swipeEnabled={false}>
2106
+ <TestComponent />
2107
+ </ToastProvider>
2108
+ );
2109
+
2110
+ await waitFor(() => {
2111
+ expect(toastRef?.show).toBeDefined();
2112
+ });
2113
+
2114
+ await act(async () => {
2115
+ toastRef?.show("Center rendered", {placement: "center"});
2116
+ });
2117
+
2118
+ // Flush the requestAnimationFrame (mocked as setTimeout(, 0)) and re-renders
2119
+ for (let i = 0; i < 5; i++) {
2120
+ await act(async () => {
2121
+ await new Promise((resolve) => setTimeout(resolve, 50));
2122
+ });
2123
+ }
2124
+
2125
+ expect(getByText("Center rendered")).toBeTruthy();
2126
+ });
2127
+
2128
+ it("should render multiple center toasts through filter and map", async () => {
2129
+ let toastRef: ToastType | null = null;
2130
+ const TestComponent = () => {
2131
+ const toast = useToastNotifications();
2132
+ toastRef = toast;
2133
+ return <Text>Test</Text>;
2134
+ };
2135
+
2136
+ const {getByText} = render(
2137
+ <ToastProvider placement="center" swipeEnabled={false}>
2138
+ <TestComponent />
2139
+ </ToastProvider>
2140
+ );
2141
+
2142
+ await waitFor(() => {
2143
+ expect(toastRef?.show).toBeDefined();
2144
+ });
2145
+
2146
+ await act(async () => {
2147
+ toastRef?.show("Center X", {id: "cx", placement: "center"});
2148
+ });
2149
+
2150
+ for (let i = 0; i < 5; i++) {
2151
+ await act(async () => {
2152
+ await new Promise((resolve) => setTimeout(resolve, 50));
2153
+ });
2154
+ }
2155
+
2156
+ await act(async () => {
2157
+ toastRef?.show("Center Y", {id: "cy", placement: "center"});
2158
+ });
2159
+
2160
+ for (let i = 0; i < 5; i++) {
2161
+ await act(async () => {
2162
+ await new Promise((resolve) => setTimeout(resolve, 50));
2163
+ });
2164
+ }
2165
+
2166
+ expect(getByText("Center X")).toBeTruthy();
2167
+ expect(getByText("Center Y")).toBeTruthy();
2168
+ });
2169
+ });
2170
+
2171
+ describe("auto-close timer callback", () => {
2172
+ it("should fire the setTimeout callback that calls handleClose", async () => {
2173
+ let toastRef: ToastType | null = null;
2174
+ const onCloseMock = mock(() => {});
2175
+
2176
+ const TestComponent = () => {
2177
+ const toast = useToastNotifications();
2178
+ toastRef = toast;
2179
+ return <Text>Test</Text>;
2180
+ };
2181
+
2182
+ render(
2183
+ <ToastProvider swipeEnabled={false}>
2184
+ <TestComponent />
2185
+ </ToastProvider>
2186
+ );
2187
+
2188
+ await waitFor(() => {
2189
+ expect(toastRef?.show).toBeDefined();
2190
+ });
2191
+
2192
+ await act(async () => {
2193
+ toastRef?.show("Timer toast", {
2194
+ duration: 50,
2195
+ id: "timer-toast",
2196
+ onClose: onCloseMock,
2197
+ });
2198
+ });
2199
+
2200
+ // Flush RAF + duration timeout + close animation
2201
+ for (let i = 0; i < 15; i++) {
2202
+ await act(async () => {
2203
+ await new Promise((resolve) => setTimeout(resolve, 50));
2204
+ });
2205
+ }
2206
+ });
2207
+ });
2208
+
2209
+ describe("onHide callback on toast props", () => {
2210
+ it("should exercise the onHide function via renderToast", async () => {
2211
+ let toastRef: ToastType | null = null;
2212
+ let capturedOnHide: (() => void) | null = null;
2213
+
2214
+ const customRenderToast = (toast: ToastProps) => {
2215
+ if (!capturedOnHide) {
2216
+ capturedOnHide = toast.onHide;
2217
+ }
2218
+ return <Text>{String(toast.message)}</Text>;
2219
+ };
2220
+
2221
+ const TestComponent = () => {
2222
+ const toast = useToastNotifications();
2223
+ toastRef = toast;
2224
+ return <Text>Test</Text>;
2225
+ };
2226
+
2227
+ render(
2228
+ <ToastProvider renderToast={customRenderToast} swipeEnabled={false}>
2229
+ <TestComponent />
2230
+ </ToastProvider>
2231
+ );
2232
+
2233
+ await waitFor(() => {
2234
+ expect(toastRef?.show).toBeDefined();
2235
+ });
2236
+
2237
+ await act(async () => {
2238
+ toastRef?.show("Custom toast", {id: "onhide-test"});
2239
+ });
2240
+
2241
+ // Flush RAF and let the toast render with the custom renderer
2242
+ for (let i = 0; i < 5; i++) {
2243
+ await act(async () => {
2244
+ await new Promise((resolve) => setTimeout(resolve, 50));
2245
+ });
2246
+ }
2247
+
2248
+ // Call the captured onHide to exercise the () => hide(id) callback
2249
+ if (capturedOnHide) {
2250
+ await act(async () => {
2251
+ capturedOnHide!();
2252
+ });
2253
+
2254
+ // Let the hide animation complete
2255
+ for (let i = 0; i < 5; i++) {
2256
+ await act(async () => {
2257
+ await new Promise((resolve) => setTimeout(resolve, 50));
2258
+ });
2259
+ }
2260
+ }
2261
+ });
2262
+ });
851
2263
  });