@applicaster/zapp-react-native-utils 15.0.0-alpha.8680244503 → 15.0.0-alpha.9102777840
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 +0 -6
- package/adsUtils/__tests__/createVMAP.test.ts +419 -0
- package/analyticsUtils/analyticsMapper.ts +10 -2
- package/appDataUtils/__tests__/urlScheme.test.ts +678 -0
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +0 -188
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +16 -2
- package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
- package/appUtils/accessibilityManager/__tests__/utils.test.ts +360 -0
- package/appUtils/accessibilityManager/utils.ts +25 -5
- package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
- package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
- package/appUtils/contextKeysManager/contextResolver.ts +51 -22
- package/appUtils/contextKeysManager/index.ts +65 -10
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +3 -0
- package/appUtils/focusManager/index.ios.ts +43 -4
- package/appUtils/focusManager/treeDataStructure/Tree/__tests__/Tree.test.js +46 -0
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +18 -18
- package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
- package/appUtils/focusManagerAux/utils/index.ts +3 -5
- package/appUtils/focusManagerAux/utils/utils.ios.ts +202 -3
- package/appUtils/playerManager/playerNative.ts +2 -1
- package/cloudEventsUtils/__tests__/index.test.ts +529 -0
- package/cloudEventsUtils/index.ts +65 -1
- package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +38 -0
- package/configurationUtils/index.ts +17 -11
- package/dateUtils/__tests__/dayjs.test.ts +330 -0
- package/enumUtils/__tests__/getEnumKeyByEnumValue.test.ts +207 -0
- package/errorUtils/__tests__/GeneralError.test.ts +97 -0
- package/errorUtils/__tests__/HttpStatusCode.test.ts +344 -0
- package/errorUtils/__tests__/MissingPluginError.test.ts +113 -0
- package/errorUtils/__tests__/NetworkError.test.ts +202 -0
- package/errorUtils/__tests__/getParsedResponse.test.ts +188 -0
- package/errorUtils/__tests__/invariant.test.ts +112 -0
- package/focusManager/aux/index.ts +1 -1
- package/headersUtils/__tests__/headersUtils.test.js +11 -1
- package/headersUtils/index.ts +2 -1
- package/manifestUtils/defaultManifestConfigurations/player.js +40 -10
- package/manifestUtils/platformIsTV.js +13 -0
- package/navigationUtils/index.ts +15 -5
- package/numberUtils/__tests__/toNumber.test.ts +27 -0
- package/numberUtils/__tests__/toPositiveNumber.test.ts +193 -0
- package/numberUtils/index.ts +23 -1
- package/package.json +4 -4
- package/reactHooks/analytics/__tests__/useSendAnalyticsOnPress.test.ts +537 -0
- package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
- package/reactHooks/cell-click/__tests__/index.test.js +1 -3
- package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
- package/reactHooks/connection/__tests__/index.test.js +1 -1
- package/reactHooks/dev/__tests__/useReRenderLog.test.ts +188 -0
- package/reactHooks/device/useIsTablet.tsx +14 -19
- package/reactHooks/events/index.ts +20 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
- package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +1 -1
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
- package/reactHooks/feed/__tests__/{useInflatedUrl.test.ts → useInflatedUrl.test.tsx} +62 -7
- package/reactHooks/feed/index.ts +0 -2
- package/reactHooks/feed/useInflatedUrl.ts +43 -17
- package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +12 -8
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/index.test.tsx +1 -1
- package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
- package/reactHooks/navigation/__tests__/index.test.tsx +40 -9
- package/reactHooks/navigation/index.ts +19 -4
- package/reactHooks/navigation/useRoute.ts +3 -1
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
- package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
- package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
- package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
- package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +2 -2
- package/reactHooks/state/__tests__/useComponentScreenState.test.ts +246 -0
- package/reactHooks/state/index.ts +2 -0
- package/reactHooks/state/useComponentScreenState.ts +45 -0
- package/reactHooks/state/useRefWithInitialValue.ts +10 -0
- package/reactHooks/ui/__tests__/useFadeOutWhenBlurred.test.ts +580 -0
- package/reactHooks/utils/__tests__/index.test.js +1 -1
- package/rectUtils/__tests__/index.test.ts +549 -0
- package/rectUtils/index.ts +2 -2
- package/refreshUtils/RefreshCoordinator/__tests__/refreshCoordinator.test.ts +161 -0
- package/refreshUtils/RefreshCoordinator/index.ts +216 -0
- package/refreshUtils/RefreshCoordinator/utils/__tests__/getDataRefreshConfig.test.ts +104 -0
- package/refreshUtils/RefreshCoordinator/utils/index.ts +29 -0
- package/screenPickerUtils/__tests__/index.test.ts +333 -0
- package/screenPickerUtils/index.ts +5 -0
- package/screenState/__tests__/index.test.ts +1 -1
- package/screenUtils/index.ts +3 -0
- package/searchUtils/const.ts +7 -0
- package/searchUtils/index.ts +3 -0
- package/services/storageServiceSync.web.ts +1 -1
- package/stringUtils/index.ts +1 -1
- package/testUtils/index.tsx +1 -1
- package/time/__tests__/BackgroundTimer.test.ts +156 -0
- package/time/__tests__/Timer.test.ts +236 -0
- package/typeGuards/__tests__/isString.test.ts +21 -0
- package/typeGuards/index.ts +4 -0
- package/utils/__tests__/clone.test.ts +158 -0
- package/utils/__tests__/mergeRight.test.ts +48 -0
- package/utils/__tests__/path.test.ts +7 -0
- package/utils/clone.ts +7 -0
- package/utils/index.ts +12 -1
- package/utils/mergeRight.ts +5 -0
- package/utils/path.ts +6 -3
- package/utils/pathOr.ts +5 -1
- package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +19 -5
- package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
- package/reactHooks/componentsMap/index.ts +0 -55
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +0 -75
- package/reactHooks/feed/useFeedRefresh.tsx +0 -65
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Timer } from "../Timer";
|
|
2
|
+
import BackgroundTimer from "../BackgroundTimer";
|
|
3
|
+
|
|
4
|
+
// Mock BackgroundTimer
|
|
5
|
+
jest.mock("../BackgroundTimer", () => ({
|
|
6
|
+
__esModule: true,
|
|
7
|
+
default: {
|
|
8
|
+
setTimeout: jest.fn((callback, delay) => {
|
|
9
|
+
return setTimeout(callback, delay);
|
|
10
|
+
}),
|
|
11
|
+
clearTimeout: jest.fn((id) => {
|
|
12
|
+
clearTimeout(id as NodeJS.Timeout);
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe("Timer", () => {
|
|
18
|
+
let mockCallback: jest.Mock;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
jest.useFakeTimers();
|
|
23
|
+
mockCallback = jest.fn();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
jest.useRealTimers();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("constructor", () => {
|
|
31
|
+
it("should create timer with callback and remaining time", () => {
|
|
32
|
+
const timer = new Timer(mockCallback, 1000);
|
|
33
|
+
|
|
34
|
+
expect(timer).toBeInstanceOf(Timer);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should create timer with callback only (optional remaining param)", () => {
|
|
38
|
+
const timer = new Timer(mockCallback);
|
|
39
|
+
|
|
40
|
+
expect(timer).toBeInstanceOf(Timer);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should not start automatically", () => {
|
|
44
|
+
// eslint-disable-next-line no-new
|
|
45
|
+
new Timer(mockCallback, 1000);
|
|
46
|
+
|
|
47
|
+
expect(BackgroundTimer.setTimeout).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("start", () => {
|
|
52
|
+
it("should call setTimeout with callback and remaining time", () => {
|
|
53
|
+
const timer = new Timer(mockCallback, 1000);
|
|
54
|
+
|
|
55
|
+
timer.start();
|
|
56
|
+
|
|
57
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
|
|
58
|
+
mockCallback,
|
|
59
|
+
1000
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should reset remaining time to 0 after starting", () => {
|
|
64
|
+
const timer = new Timer(mockCallback, 5000);
|
|
65
|
+
|
|
66
|
+
timer.start();
|
|
67
|
+
timer.clear();
|
|
68
|
+
timer.start();
|
|
69
|
+
|
|
70
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("pause", () => {
|
|
75
|
+
it("should clear timeout and calculate remaining time correctly", () => {
|
|
76
|
+
const startTime = 1000;
|
|
77
|
+
const pauseTime = 1500;
|
|
78
|
+
|
|
79
|
+
jest
|
|
80
|
+
.spyOn(Date, "now")
|
|
81
|
+
.mockReturnValueOnce(startTime) // start
|
|
82
|
+
.mockReturnValueOnce(pauseTime); // pause
|
|
83
|
+
|
|
84
|
+
const timer = new Timer(mockCallback, 2000);
|
|
85
|
+
|
|
86
|
+
timer.start();
|
|
87
|
+
timer.pause();
|
|
88
|
+
|
|
89
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalled();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should be safe to call pause multiple times", () => {
|
|
93
|
+
const timer = new Timer(mockCallback, 1000);
|
|
94
|
+
|
|
95
|
+
timer.start();
|
|
96
|
+
timer.pause();
|
|
97
|
+
timer.pause();
|
|
98
|
+
|
|
99
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("resume", () => {
|
|
104
|
+
it("should restart timer with remaining time after pause", () => {
|
|
105
|
+
const startTime = 1000;
|
|
106
|
+
const pauseTime = 1500;
|
|
107
|
+
|
|
108
|
+
jest
|
|
109
|
+
.spyOn(Date, "now")
|
|
110
|
+
.mockReturnValueOnce(startTime)
|
|
111
|
+
.mockReturnValueOnce(pauseTime);
|
|
112
|
+
|
|
113
|
+
const timer = new Timer(mockCallback, 2000);
|
|
114
|
+
|
|
115
|
+
timer.start();
|
|
116
|
+
timer.pause();
|
|
117
|
+
timer.resume();
|
|
118
|
+
|
|
119
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should be callable without prior pause", () => {
|
|
123
|
+
const timer = new Timer(mockCallback, 1000);
|
|
124
|
+
|
|
125
|
+
timer.resume();
|
|
126
|
+
|
|
127
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("clear", () => {
|
|
132
|
+
it("should clear timeout and be safe to call without starting timer", () => {
|
|
133
|
+
const timer = new Timer(mockCallback, 1000);
|
|
134
|
+
|
|
135
|
+
expect(() => {
|
|
136
|
+
timer.clear();
|
|
137
|
+
}).not.toThrow();
|
|
138
|
+
|
|
139
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should be callable multiple times", () => {
|
|
143
|
+
const timer = new Timer(mockCallback, 1000);
|
|
144
|
+
|
|
145
|
+
timer.start();
|
|
146
|
+
timer.clear();
|
|
147
|
+
timer.clear();
|
|
148
|
+
|
|
149
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("timer lifecycle", () => {
|
|
154
|
+
it("should support complete lifecycle: start -> pause -> resume -> clear", () => {
|
|
155
|
+
const timer = new Timer(mockCallback, 1000);
|
|
156
|
+
|
|
157
|
+
timer.start();
|
|
158
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(1);
|
|
159
|
+
|
|
160
|
+
timer.pause();
|
|
161
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(1);
|
|
162
|
+
|
|
163
|
+
timer.resume();
|
|
164
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledTimes(2);
|
|
165
|
+
|
|
166
|
+
timer.clear();
|
|
167
|
+
expect(BackgroundTimer.clearTimeout).toHaveBeenCalledTimes(2);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe("edge cases", () => {
|
|
172
|
+
it("should handle zero delay", () => {
|
|
173
|
+
const timer = new Timer(mockCallback, 0);
|
|
174
|
+
|
|
175
|
+
timer.start();
|
|
176
|
+
|
|
177
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(mockCallback, 0);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should handle very long delays", () => {
|
|
181
|
+
const longDelay = Number.MAX_SAFE_INTEGER;
|
|
182
|
+
const timer = new Timer(mockCallback, longDelay);
|
|
183
|
+
|
|
184
|
+
timer.start();
|
|
185
|
+
|
|
186
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
|
|
187
|
+
mockCallback,
|
|
188
|
+
longDelay
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should handle negative delays gracefully", () => {
|
|
193
|
+
const timer = new Timer(mockCallback, -1000);
|
|
194
|
+
|
|
195
|
+
expect(() => {
|
|
196
|
+
timer.start();
|
|
197
|
+
}).not.toThrow();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should maintain callback reference through lifecycle", () => {
|
|
201
|
+
const specificCallback = jest.fn();
|
|
202
|
+
const timer = new Timer(specificCallback, 1000);
|
|
203
|
+
|
|
204
|
+
timer.start();
|
|
205
|
+
|
|
206
|
+
expect(BackgroundTimer.setTimeout).toHaveBeenCalledWith(
|
|
207
|
+
specificCallback,
|
|
208
|
+
1000
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("callback execution safety", () => {
|
|
214
|
+
it("should not execute callback immediately on start", () => {
|
|
215
|
+
const timer = new Timer(mockCallback, 1000);
|
|
216
|
+
|
|
217
|
+
timer.start();
|
|
218
|
+
|
|
219
|
+
expect(mockCallback).not.toHaveBeenCalled();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("should not execute callback when paused or cleared", () => {
|
|
223
|
+
const timer1 = new Timer(mockCallback, 100);
|
|
224
|
+
const timer2 = new Timer(mockCallback, 100);
|
|
225
|
+
|
|
226
|
+
timer1.start();
|
|
227
|
+
timer1.pause();
|
|
228
|
+
timer2.start();
|
|
229
|
+
timer2.clear();
|
|
230
|
+
|
|
231
|
+
jest.advanceTimersByTime(200);
|
|
232
|
+
|
|
233
|
+
expect(mockCallback).not.toHaveBeenCalled();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isString } from "../index";
|
|
2
|
+
|
|
3
|
+
describe("isString", () => {
|
|
4
|
+
it("should return true for string primitives", () => {
|
|
5
|
+
expect(isString("hello")).toBe(true);
|
|
6
|
+
expect(isString("")).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return true for String object instances", () => {
|
|
10
|
+
// eslint-disable-next-line no-new-wrappers
|
|
11
|
+
expect(isString(new String("test"))).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false for non-string values", () => {
|
|
15
|
+
expect(isString(123)).toBe(false);
|
|
16
|
+
expect(isString(null)).toBe(false);
|
|
17
|
+
expect(isString(undefined)).toBe(false);
|
|
18
|
+
expect(isString({})).toBe(false);
|
|
19
|
+
expect(isString([])).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
package/typeGuards/index.ts
CHANGED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { clone } from "../clone";
|
|
2
|
+
|
|
3
|
+
describe("clone", () => {
|
|
4
|
+
const originalStructuredClone = global.structuredClone;
|
|
5
|
+
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
// Restore the original structuredClone after each test
|
|
8
|
+
global.structuredClone = originalStructuredClone;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe("when structuredClone is available", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
// Mock structuredClone if it's not available
|
|
14
|
+
if (typeof global.structuredClone === "undefined") {
|
|
15
|
+
global.structuredClone = jest.fn((value) =>
|
|
16
|
+
JSON.parse(JSON.stringify(value))
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("clones primitive values", () => {
|
|
22
|
+
expect(clone(42)).toBe(42);
|
|
23
|
+
expect(clone("test")).toBe("test");
|
|
24
|
+
expect(clone(true)).toBe(true);
|
|
25
|
+
expect(clone(null)).toBe(null);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("clones simple objects", () => {
|
|
29
|
+
const original = { a: 1, b: 2 };
|
|
30
|
+
const cloned = clone(original);
|
|
31
|
+
|
|
32
|
+
expect(cloned).toEqual(original);
|
|
33
|
+
expect(cloned).not.toBe(original);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("clones nested objects", () => {
|
|
37
|
+
const original = { a: { b: { c: 1 } } };
|
|
38
|
+
const cloned = clone(original);
|
|
39
|
+
|
|
40
|
+
expect(cloned).toEqual(original);
|
|
41
|
+
expect(cloned).not.toBe(original);
|
|
42
|
+
expect(cloned.a).not.toBe(original.a);
|
|
43
|
+
expect(cloned.a.b).not.toBe(original.a.b);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("clones arrays", () => {
|
|
47
|
+
const original = [1, 2, 3];
|
|
48
|
+
const cloned = clone(original);
|
|
49
|
+
|
|
50
|
+
expect(cloned).toEqual(original);
|
|
51
|
+
expect(cloned).not.toBe(original);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("clones arrays with nested objects", () => {
|
|
55
|
+
const original = [{ a: 1 }, { b: 2 }];
|
|
56
|
+
const cloned = clone(original);
|
|
57
|
+
|
|
58
|
+
expect(cloned).toEqual(original);
|
|
59
|
+
expect(cloned).not.toBe(original);
|
|
60
|
+
expect(cloned[0]).not.toBe(original[0]);
|
|
61
|
+
expect(cloned[1]).not.toBe(original[1]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("does not mutate the original value", () => {
|
|
65
|
+
const original = { a: 1, b: { c: 2 } };
|
|
66
|
+
const cloned = clone(original);
|
|
67
|
+
|
|
68
|
+
cloned.a = 99;
|
|
69
|
+
cloned.b.c = 99;
|
|
70
|
+
|
|
71
|
+
expect(original.a).toBe(1);
|
|
72
|
+
expect(original.b.c).toBe(2);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("when structuredClone is not available (fallback to cloneDeep)", () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
// Delete structuredClone to force fallback to cloneDeep
|
|
79
|
+
delete (global as any).structuredClone;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("clones primitive values using cloneDeep", () => {
|
|
83
|
+
expect(clone(42)).toBe(42);
|
|
84
|
+
expect(clone("test")).toBe("test");
|
|
85
|
+
expect(clone(true)).toBe(true);
|
|
86
|
+
expect(clone(null)).toBe(null);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("clones simple objects using cloneDeep", () => {
|
|
90
|
+
const original = { a: 1, b: 2 };
|
|
91
|
+
const cloned = clone(original);
|
|
92
|
+
|
|
93
|
+
expect(cloned).toEqual(original);
|
|
94
|
+
expect(cloned).not.toBe(original);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("clones nested objects using cloneDeep", () => {
|
|
98
|
+
const original = { a: { b: { c: 1 } } };
|
|
99
|
+
const cloned = clone(original);
|
|
100
|
+
|
|
101
|
+
expect(cloned).toEqual(original);
|
|
102
|
+
expect(cloned).not.toBe(original);
|
|
103
|
+
expect(cloned.a).not.toBe(original.a);
|
|
104
|
+
expect(cloned.a.b).not.toBe(original.a.b);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("clones arrays using cloneDeep", () => {
|
|
108
|
+
const original = [1, 2, 3];
|
|
109
|
+
const cloned = clone(original);
|
|
110
|
+
|
|
111
|
+
expect(cloned).toEqual(original);
|
|
112
|
+
expect(cloned).not.toBe(original);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("clones arrays with nested objects using cloneDeep", () => {
|
|
116
|
+
const original = [{ a: 1 }, { b: 2 }];
|
|
117
|
+
const cloned = clone(original);
|
|
118
|
+
|
|
119
|
+
expect(cloned).toEqual(original);
|
|
120
|
+
expect(cloned).not.toBe(original);
|
|
121
|
+
expect(cloned[0]).not.toBe(original[0]);
|
|
122
|
+
expect(cloned[1]).not.toBe(original[1]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("does not mutate the original value when using cloneDeep", () => {
|
|
126
|
+
const original = { a: 1, b: { c: 2 } };
|
|
127
|
+
const cloned = clone(original);
|
|
128
|
+
|
|
129
|
+
cloned.a = 99;
|
|
130
|
+
cloned.b.c = 99;
|
|
131
|
+
|
|
132
|
+
expect(original.a).toBe(1);
|
|
133
|
+
expect(original.b.c).toBe(2);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("handles complex objects with multiple levels", () => {
|
|
137
|
+
const original = {
|
|
138
|
+
name: "test",
|
|
139
|
+
data: {
|
|
140
|
+
items: [
|
|
141
|
+
{ id: 1, value: "a" },
|
|
142
|
+
{ id: 2, value: "b" },
|
|
143
|
+
],
|
|
144
|
+
meta: { count: 2 },
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const cloned = clone(original);
|
|
149
|
+
|
|
150
|
+
expect(cloned).toEqual(original);
|
|
151
|
+
expect(cloned).not.toBe(original);
|
|
152
|
+
expect(cloned.data).not.toBe(original.data);
|
|
153
|
+
expect(cloned.data.items).not.toBe(original.data.items);
|
|
154
|
+
expect(cloned.data.items[0]).not.toBe(original.data.items[0]);
|
|
155
|
+
expect(cloned.data.meta).not.toBe(original.data.meta);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { mergeRight } from "../mergeRight";
|
|
2
|
+
|
|
3
|
+
describe("mergeRight", () => {
|
|
4
|
+
test("merges two objects with no overlapping keys", () => {
|
|
5
|
+
const a = { x: 1, y: 2 };
|
|
6
|
+
const b = { z: 3 };
|
|
7
|
+
|
|
8
|
+
const result = mergeRight(a, b);
|
|
9
|
+
|
|
10
|
+
expect(result).toEqual({ x: 1, y: 2, z: 3 });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("overwrites keys from the second object", () => {
|
|
14
|
+
const a = { x: 1, y: 2 };
|
|
15
|
+
const b = { y: 10, z: 3 };
|
|
16
|
+
|
|
17
|
+
const result = mergeRight(a, b);
|
|
18
|
+
|
|
19
|
+
expect(result).toEqual({ x: 1, y: 10, z: 3 });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("does not mutate the original objects", () => {
|
|
23
|
+
const a = { x: 1 };
|
|
24
|
+
const b = { y: 2 };
|
|
25
|
+
|
|
26
|
+
const result = mergeRight(a, b);
|
|
27
|
+
|
|
28
|
+
expect(result).not.toBe(a);
|
|
29
|
+
expect(result).not.toBe(b);
|
|
30
|
+
expect(a).toEqual({ x: 1 });
|
|
31
|
+
expect(b).toEqual({ y: 2 });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("works with empty objects", () => {
|
|
35
|
+
expect(mergeRight({}, { a: 1 })).toEqual({ a: 1 });
|
|
36
|
+
expect(mergeRight({ a: 1 }, {})).toEqual({ a: 1 });
|
|
37
|
+
expect(mergeRight({}, {})).toEqual({});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("works with nested objects (shallow merge only)", () => {
|
|
41
|
+
const a = { x: { nested: 1 }, y: 2 };
|
|
42
|
+
const b = { x: { nested: 10 }, z: 3 };
|
|
43
|
+
|
|
44
|
+
const result = mergeRight(a, b);
|
|
45
|
+
|
|
46
|
+
expect(result).toEqual({ x: { nested: 10 }, y: 2, z: 3 });
|
|
47
|
+
});
|
|
48
|
+
});
|
package/utils/clone.ts
ADDED
package/utils/index.ts
CHANGED
|
@@ -18,8 +18,11 @@ export { take } from "./take";
|
|
|
18
18
|
|
|
19
19
|
export { mapAccum } from "./mapAccum";
|
|
20
20
|
|
|
21
|
+
export { mergeRight } from "./mergeRight";
|
|
22
|
+
|
|
23
|
+
export { clone } from "./clone";
|
|
24
|
+
|
|
21
25
|
export {
|
|
22
|
-
cloneDeep as clone,
|
|
23
26
|
flatten,
|
|
24
27
|
drop,
|
|
25
28
|
size,
|
|
@@ -42,6 +45,14 @@ export {
|
|
|
42
45
|
partial,
|
|
43
46
|
clamp,
|
|
44
47
|
reverse,
|
|
48
|
+
takeRight,
|
|
49
|
+
fromPairs,
|
|
50
|
+
sortBy,
|
|
51
|
+
merge,
|
|
52
|
+
values,
|
|
53
|
+
head,
|
|
54
|
+
findIndex,
|
|
45
55
|
set,
|
|
46
56
|
compact,
|
|
57
|
+
identity,
|
|
47
58
|
} from "lodash";
|
package/utils/path.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pathOr } from "./pathOr";
|
|
2
2
|
|
|
3
|
-
export const path =
|
|
4
|
-
|
|
3
|
+
export const path = <T = any>(
|
|
4
|
+
route: (string | number) | (string | number)[],
|
|
5
|
+
record: any
|
|
6
|
+
) => {
|
|
7
|
+
return pathOr<T>(undefined, route, record);
|
|
5
8
|
};
|
package/utils/pathOr.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { get } from "lodash";
|
|
2
2
|
|
|
3
|
-
export const pathOr =
|
|
3
|
+
export const pathOr = <T = any>(
|
|
4
|
+
defaultValue: T,
|
|
5
|
+
path: (number | string) | (number | string)[],
|
|
6
|
+
record: any
|
|
7
|
+
): T => {
|
|
4
8
|
return get(record, path, defaultValue);
|
|
5
9
|
};
|
|
@@ -46,7 +46,7 @@ const legacyMappingKeys = {
|
|
|
46
46
|
actionType: "login_completion_action",
|
|
47
47
|
targetScreen: "navigate_to_login_screen",
|
|
48
48
|
},
|
|
49
|
-
"quick-brick-user-account-ui-component": {
|
|
49
|
+
"quick-brick-user-account-ui-component.login": {
|
|
50
50
|
actionType: "callbackAction",
|
|
51
51
|
},
|
|
52
52
|
"quick-brick-login-multi-login-providers.login": {
|
|
@@ -84,7 +84,8 @@ export const getNavigationKeys = (
|
|
|
84
84
|
): NavKeys => {
|
|
85
85
|
const general = (item?.general ?? {}) as General;
|
|
86
86
|
|
|
87
|
-
const pluginIdentifier =
|
|
87
|
+
const pluginIdentifier =
|
|
88
|
+
(item as any).identifier ?? item?.type ?? item?.component_type ?? "";
|
|
88
89
|
|
|
89
90
|
const legacy =
|
|
90
91
|
legacyMappingKeys[`${pluginIdentifier}.${resultType}`] ??
|
|
@@ -175,12 +176,25 @@ export const useCallbackNavigationAction = (
|
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
let data = getNavigationKeys(item, args.options?.resultType ?? null);
|
|
179
180
|
|
|
180
181
|
if (!data) {
|
|
181
|
-
hookCallback
|
|
182
|
+
const isScreen = !hookCallback;
|
|
182
183
|
|
|
183
|
-
|
|
184
|
+
if (isScreen && args.options?.resultType === ResultType.login) {
|
|
185
|
+
log_debug(
|
|
186
|
+
`${LogPrefix} no navigation data found, applying GO BACK for login screen`
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
data = {
|
|
190
|
+
action: NavigationCallbackOptions.GO_BACK,
|
|
191
|
+
targetScreenId: null,
|
|
192
|
+
};
|
|
193
|
+
} else {
|
|
194
|
+
hookCallback?.(args);
|
|
195
|
+
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
184
198
|
}
|
|
185
199
|
|
|
186
200
|
hookCallback?.({ ...args, success: false, cancelled: true });
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
const layoutReducer = (state, { payload }) => {
|
|
4
|
-
return state.map((item, index, _state) => ({
|
|
5
|
-
height: index === payload.index ? payload.height : item.height,
|
|
6
|
-
y:
|
|
7
|
-
index > 0
|
|
8
|
-
? _state.slice(0, index).reduce((acc, value) => acc + value.height, 0)
|
|
9
|
-
: 0,
|
|
10
|
-
}));
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const useComponentsLayout = (count, onFinishLoadingVisible) => {
|
|
14
|
-
const [itemsLayout, dispatchItemLayout] = React.useReducer(
|
|
15
|
-
layoutReducer,
|
|
16
|
-
Array.from({ length: count }, () => ({ height: 0, y: 0 }))
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const notified = React.useRef(false);
|
|
20
|
-
const [listHeight, setListHeight] = React.useState<number>(0);
|
|
21
|
-
|
|
22
|
-
const onItemLayout = React.useCallback(
|
|
23
|
-
(index) => (event) => {
|
|
24
|
-
const { height } = event.nativeEvent.layout;
|
|
25
|
-
dispatchItemLayout({ payload: { index, height } });
|
|
26
|
-
},
|
|
27
|
-
[]
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const onListLayout = React.useCallback((event) => {
|
|
31
|
-
const { height } = event.nativeEvent.layout;
|
|
32
|
-
setListHeight(height);
|
|
33
|
-
}, []);
|
|
34
|
-
|
|
35
|
-
React.useEffect(() => {
|
|
36
|
-
if (!notified.current) {
|
|
37
|
-
const finishLoadingVisible = !!itemsLayout.find(
|
|
38
|
-
(item) => item.y > listHeight
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (finishLoadingVisible) {
|
|
42
|
-
notified.current = true;
|
|
43
|
-
onFinishLoadingVisible?.();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}, [itemsLayout, listHeight]);
|
|
47
|
-
|
|
48
|
-
return React.useMemo(
|
|
49
|
-
() => ({
|
|
50
|
-
onItemLayout,
|
|
51
|
-
onListLayout,
|
|
52
|
-
}),
|
|
53
|
-
[]
|
|
54
|
-
);
|
|
55
|
-
};
|