@rn-tools/navigation 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +422 -0
- package/package.json +44 -0
- package/src/__tests__/navigation-reducer.test.tsx +348 -0
- package/src/contexts.tsx +8 -0
- package/src/index.ts +3 -0
- package/src/navigation-reducer.ts +467 -0
- package/src/navigation-store.ts +55 -0
- package/src/navigation.tsx +141 -0
- package/src/stack.tsx +255 -0
- package/src/tabs.tsx +297 -0
- package/src/types.ts +48 -0
- package/src/utils.ts +14 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import type { NavigationState, RenderCharts } from "~/types";
|
|
2
|
+
|
|
3
|
+
import { reducer, type NavigationAction } from "../navigation-reducer";
|
|
4
|
+
import { serializeTabIndexKey } from "../utils";
|
|
5
|
+
|
|
6
|
+
describe("reducer", () => {
|
|
7
|
+
let initialState: NavigationState = {
|
|
8
|
+
stacks: {
|
|
9
|
+
ids: [],
|
|
10
|
+
lookup: {},
|
|
11
|
+
},
|
|
12
|
+
screens: {
|
|
13
|
+
ids: [],
|
|
14
|
+
lookup: {},
|
|
15
|
+
},
|
|
16
|
+
tabs: {
|
|
17
|
+
ids: [],
|
|
18
|
+
lookup: {},
|
|
19
|
+
},
|
|
20
|
+
debugModeEnabled: false,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let renderCharts: RenderCharts = {
|
|
24
|
+
stacksByDepth: {},
|
|
25
|
+
tabsByDepth: {},
|
|
26
|
+
tabParentsById: {},
|
|
27
|
+
stackParentsById: {},
|
|
28
|
+
stacksByTabIndex: {},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let context = { renderCharts };
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
initialState = {
|
|
35
|
+
stacks: {
|
|
36
|
+
ids: [],
|
|
37
|
+
lookup: {},
|
|
38
|
+
},
|
|
39
|
+
screens: {
|
|
40
|
+
ids: [],
|
|
41
|
+
lookup: {},
|
|
42
|
+
},
|
|
43
|
+
tabs: {
|
|
44
|
+
ids: [],
|
|
45
|
+
lookup: {},
|
|
46
|
+
},
|
|
47
|
+
debugModeEnabled: false,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
renderCharts = {
|
|
51
|
+
stacksByDepth: {},
|
|
52
|
+
tabsByDepth: {},
|
|
53
|
+
tabParentsById: {},
|
|
54
|
+
stackParentsById: {},
|
|
55
|
+
stacksByTabIndex: {},
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should handle CREATE_STACK_INSTANCE", () => {
|
|
60
|
+
let action: NavigationAction = {
|
|
61
|
+
type: "CREATE_STACK_INSTANCE",
|
|
62
|
+
stackId: "stack1",
|
|
63
|
+
};
|
|
64
|
+
let newState = reducer(initialState, action, context);
|
|
65
|
+
|
|
66
|
+
expect(newState.stacks.ids).toContain("stack1");
|
|
67
|
+
expect(newState.stacks.lookup["stack1"]).toEqual({
|
|
68
|
+
id: "stack1",
|
|
69
|
+
defaultSlotName: "DEFAULT_SLOT",
|
|
70
|
+
screens: [],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should handle REGISTER_STACK", () => {
|
|
75
|
+
let action: NavigationAction = {
|
|
76
|
+
type: "REGISTER_STACK",
|
|
77
|
+
depth: 1,
|
|
78
|
+
isActive: true,
|
|
79
|
+
stackId: "stack1",
|
|
80
|
+
parentStackId: "parentStack1",
|
|
81
|
+
parentTabId: "parentTab1",
|
|
82
|
+
tabIndex: 0,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
reducer(initialState, action, context);
|
|
86
|
+
|
|
87
|
+
expect(context.renderCharts.stacksByDepth[1]).toContain("stack1");
|
|
88
|
+
expect(context.renderCharts.stackParentsById["stack1"]).toBe(
|
|
89
|
+
"parentStack1"
|
|
90
|
+
);
|
|
91
|
+
let tabIndexKey = serializeTabIndexKey("parentTab1", 0);
|
|
92
|
+
expect(context.renderCharts.stacksByTabIndex[tabIndexKey]).toContain(
|
|
93
|
+
"stack1"
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should handle UNREGISTER_STACK", () => {
|
|
98
|
+
let initialStateWithStack: NavigationState = {
|
|
99
|
+
...initialState,
|
|
100
|
+
stacks: {
|
|
101
|
+
ids: ["stack1"],
|
|
102
|
+
lookup: {
|
|
103
|
+
stack1: {
|
|
104
|
+
id: "stack1",
|
|
105
|
+
defaultSlotName: "DEFAULT_SLOT",
|
|
106
|
+
screens: ["screen1", "screen2"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
screens: {
|
|
111
|
+
ids: ["screen1", "screen2"],
|
|
112
|
+
lookup: {
|
|
113
|
+
screen1: { id: "screen1", stackId: "stack1", element: null },
|
|
114
|
+
screen2: { id: "screen2", stackId: "stack1", element: null },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
let action: NavigationAction = {
|
|
120
|
+
type: "UNREGISTER_STACK",
|
|
121
|
+
stackId: "stack1",
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
let newState = reducer(initialStateWithStack, action, context);
|
|
125
|
+
|
|
126
|
+
expect(newState.stacks.ids).not.toContain("stack1");
|
|
127
|
+
expect(newState.screens.ids).not.toContain("screen1");
|
|
128
|
+
expect(newState.screens.ids).not.toContain("screen2");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should handle PUSH_SCREEN", () => {
|
|
132
|
+
let action: NavigationAction = {
|
|
133
|
+
type: "PUSH_SCREEN",
|
|
134
|
+
element: null,
|
|
135
|
+
stackId: "stack1",
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
let initialStateWithStack: NavigationState = {
|
|
139
|
+
...initialState,
|
|
140
|
+
stacks: {
|
|
141
|
+
ids: ["stack1"],
|
|
142
|
+
lookup: {
|
|
143
|
+
stack1: {
|
|
144
|
+
id: "stack1",
|
|
145
|
+
defaultSlotName: "DEFAULT_SLOT",
|
|
146
|
+
screens: [],
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
let newState = reducer(initialStateWithStack, action, context);
|
|
153
|
+
|
|
154
|
+
expect(newState.screens.ids).toHaveLength(1);
|
|
155
|
+
expect(newState.screens.lookup).toHaveProperty(newState.screens.ids[0]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should handle POP_SCREEN_BY_COUNT", () => {
|
|
159
|
+
let action: NavigationAction = {
|
|
160
|
+
type: "POP_SCREEN_BY_COUNT",
|
|
161
|
+
count: 1,
|
|
162
|
+
stackId: "stack1",
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
let initialStateWithStack: NavigationState = {
|
|
166
|
+
...initialState,
|
|
167
|
+
stacks: {
|
|
168
|
+
ids: ["stack1"],
|
|
169
|
+
lookup: {
|
|
170
|
+
stack1: {
|
|
171
|
+
id: "stack1",
|
|
172
|
+
defaultSlotName: "DEFAULT_SLOT",
|
|
173
|
+
screens: ["screen1"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
screens: {
|
|
179
|
+
ids: ["screen1"],
|
|
180
|
+
lookup: {
|
|
181
|
+
screen1: { id: "screen1", stackId: "stack1", element: null },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
let newState = reducer(initialStateWithStack, action, context);
|
|
187
|
+
|
|
188
|
+
expect(newState.stacks.lookup.stack1.screens).toHaveLength(0);
|
|
189
|
+
expect(newState.screens.ids).not.toContain("screen1");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should handle POP_SCREEN_BY_KEY", () => {
|
|
193
|
+
let action: NavigationAction = {
|
|
194
|
+
type: "POP_SCREEN_BY_KEY",
|
|
195
|
+
key: "screen1",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let initialStateWithStack: NavigationState = {
|
|
199
|
+
...initialState,
|
|
200
|
+
stacks: {
|
|
201
|
+
ids: ["stack1"],
|
|
202
|
+
lookup: {
|
|
203
|
+
stack1: {
|
|
204
|
+
id: "stack1",
|
|
205
|
+
defaultSlotName: "DEFAULT_SLOT",
|
|
206
|
+
screens: ["screen1", "screen2"],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
screens: {
|
|
212
|
+
ids: ["screen1", "screen2"],
|
|
213
|
+
lookup: {
|
|
214
|
+
screen1: { id: "screen1", stackId: "stack1", element: null },
|
|
215
|
+
screen2: { id: "screen2", stackId: "stack1", element: null },
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
let newState = reducer(initialStateWithStack, action, context);
|
|
221
|
+
console.log(newState);
|
|
222
|
+
|
|
223
|
+
expect(newState.screens.ids).not.toContain("screen1");
|
|
224
|
+
expect(newState.stacks.lookup.stack1.screens).not.toContain("screen1");
|
|
225
|
+
expect(newState.screens.ids).toContain("screen2");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// New test cases
|
|
229
|
+
it("should handle CREATE_TAB_INSTANCE", () => {
|
|
230
|
+
let action: NavigationAction = {
|
|
231
|
+
type: "CREATE_TAB_INSTANCE",
|
|
232
|
+
tabId: "tab1",
|
|
233
|
+
initialActiveIndex: 0,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
let newState = reducer(initialState, action, context);
|
|
237
|
+
|
|
238
|
+
expect(newState.tabs.ids).toContain("tab1");
|
|
239
|
+
expect(newState.tabs.lookup["tab1"]).toEqual({
|
|
240
|
+
id: "tab1",
|
|
241
|
+
activeIndex: 0,
|
|
242
|
+
history: [],
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should handle SET_TAB_INDEX", () => {
|
|
247
|
+
let action: NavigationAction = {
|
|
248
|
+
type: "SET_TAB_INDEX",
|
|
249
|
+
tabId: "tab1",
|
|
250
|
+
index: 1,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
let initialStateWithTab: NavigationState = {
|
|
254
|
+
...initialState,
|
|
255
|
+
tabs: {
|
|
256
|
+
ids: ["tab1"],
|
|
257
|
+
lookup: {
|
|
258
|
+
tab1: {
|
|
259
|
+
id: "tab1",
|
|
260
|
+
activeIndex: 0,
|
|
261
|
+
history: [],
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
let newState = reducer(initialStateWithTab, action, context);
|
|
268
|
+
|
|
269
|
+
expect(newState.tabs.lookup["tab1"].activeIndex).toBe(1);
|
|
270
|
+
expect(newState.tabs.lookup["tab1"].history).toContain(0);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should handle REGISTER_TAB", () => {
|
|
274
|
+
let action: NavigationAction = {
|
|
275
|
+
type: "REGISTER_TAB",
|
|
276
|
+
depth: 1,
|
|
277
|
+
tabId: "tab1",
|
|
278
|
+
isActive: true,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
reducer(initialState, action, context);
|
|
282
|
+
|
|
283
|
+
expect(context.renderCharts.tabsByDepth[1]).toContain("tab1");
|
|
284
|
+
expect(context.renderCharts.tabParentsById["tab1"]).toBe("");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("should handle UNREGISTER_TAB", () => {
|
|
288
|
+
let initialStateWithTab: NavigationState = {
|
|
289
|
+
...initialState,
|
|
290
|
+
tabs: {
|
|
291
|
+
ids: ["tab1"],
|
|
292
|
+
lookup: {
|
|
293
|
+
tab1: {
|
|
294
|
+
id: "tab1",
|
|
295
|
+
activeIndex: 0,
|
|
296
|
+
history: [],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
let action: NavigationAction = {
|
|
303
|
+
type: "UNREGISTER_TAB",
|
|
304
|
+
tabId: "tab1",
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
let newState = reducer(initialStateWithTab, action, context);
|
|
308
|
+
|
|
309
|
+
expect(newState.tabs.ids).not.toContain("tab1");
|
|
310
|
+
expect(newState.tabs.lookup["tab1"]).toBeUndefined();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("should handle TAB_BACK", () => {
|
|
314
|
+
let action: NavigationAction = {
|
|
315
|
+
type: "TAB_BACK",
|
|
316
|
+
tabId: "tab1",
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
let initialStateWithTab: NavigationState = {
|
|
320
|
+
...initialState,
|
|
321
|
+
tabs: {
|
|
322
|
+
ids: ["tab1"],
|
|
323
|
+
lookup: {
|
|
324
|
+
tab1: {
|
|
325
|
+
id: "tab1",
|
|
326
|
+
activeIndex: 1,
|
|
327
|
+
history: [0],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
let newState = reducer(initialStateWithTab, action, context);
|
|
334
|
+
|
|
335
|
+
expect(newState.tabs.lookup.tab1.activeIndex).toBe(0);
|
|
336
|
+
expect(newState.tabs.lookup.tab1.history).toHaveLength(0);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should handle setDebugModeEnabled', () => {
|
|
340
|
+
let action: NavigationAction = {
|
|
341
|
+
type: "SET_DEBUG_MODE",
|
|
342
|
+
enabled: true,
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
let newState = reducer(initialState, action, context);
|
|
346
|
+
expect(newState.debugModeEnabled).toBe(true);
|
|
347
|
+
})
|
|
348
|
+
});
|
package/src/contexts.tsx
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
export let StackIdContext = React.createContext<string>("");
|
|
4
|
+
export let ScreenIdContext = React.createContext<string>("");
|
|
5
|
+
export let ActiveContext = React.createContext<boolean>(true);
|
|
6
|
+
export let DepthContext = React.createContext<number>(0);
|
|
7
|
+
export let TabIdContext = React.createContext<string>("");
|
|
8
|
+
export let TabScreenIndexContext = React.createContext<number>(0);
|
package/src/index.ts
ADDED