@applicaster/zapp-react-native-ui-components 13.0.7-alpha.2991546311 → 13.0.7
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/Components/Cell/Cell.tsx +11 -29
- package/Components/Focusable/Focusable.tsx +0 -8
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +1 -5
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +0 -6
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +0 -2
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +379 -0
- package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
- package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +0 -13
- package/Components/TextInputTv/index.tsx +0 -11
- package/package.json +5 -5
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -15,7 +15,6 @@ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUt
|
|
|
15
15
|
import { styles } from "./styles";
|
|
16
16
|
|
|
17
17
|
type Props = {
|
|
18
|
-
dataLength: number;
|
|
19
18
|
item: ZappEntry;
|
|
20
19
|
index: number;
|
|
21
20
|
shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
|
|
@@ -71,8 +70,6 @@ type State = {
|
|
|
71
70
|
};
|
|
72
71
|
|
|
73
72
|
export class CellComponent extends React.Component<Props, State> {
|
|
74
|
-
accessibilityManager: AccessibilityManager;
|
|
75
|
-
|
|
76
73
|
constructor(props) {
|
|
77
74
|
super(props);
|
|
78
75
|
this.onPress = this.onPress.bind(this);
|
|
@@ -82,13 +79,10 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
82
79
|
this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
|
|
83
80
|
this.scrollVertically = this.scrollVertically.bind(this);
|
|
84
81
|
this.scrollToIndex = this.scrollToIndex.bind(this);
|
|
85
|
-
this.handleAccessibilityFocus = this.handleAccessibilityFocus.bind(this);
|
|
86
82
|
|
|
87
83
|
this.state = {
|
|
88
84
|
hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
|
|
89
85
|
};
|
|
90
|
-
|
|
91
|
-
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
92
86
|
}
|
|
93
87
|
|
|
94
88
|
setScreenLayout(componentAnchorPointY, screenLayout) {
|
|
@@ -189,24 +183,6 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
189
183
|
return !isFocusable ? false : focused || focusableFocused;
|
|
190
184
|
}
|
|
191
185
|
|
|
192
|
-
handleAccessibilityFocus(item, index, dataLength) {
|
|
193
|
-
const accessibilityTitle =
|
|
194
|
-
item?.extensions?.accessibility?.label || item?.title || "";
|
|
195
|
-
|
|
196
|
-
const accessibilityHint = item?.extensions?.accessibility?.hint || "";
|
|
197
|
-
|
|
198
|
-
// For loop scrolling, calculate the correct logical index
|
|
199
|
-
const logicalIndex = dataLength ? index % dataLength : index;
|
|
200
|
-
|
|
201
|
-
const positionLabel = dataLength
|
|
202
|
-
? `item ${logicalIndex + 1} of ${dataLength}`
|
|
203
|
-
: "";
|
|
204
|
-
|
|
205
|
-
this.accessibilityManager.readText({
|
|
206
|
-
text: `${accessibilityTitle} ${accessibilityHint} ${positionLabel}`,
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
186
|
componentDidUpdate(prevProps: Readonly<Props>) {
|
|
211
187
|
if (prevProps.item !== this.props.item) {
|
|
212
188
|
this.setState({
|
|
@@ -221,7 +197,6 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
221
197
|
const {
|
|
222
198
|
item,
|
|
223
199
|
index,
|
|
224
|
-
dataLength,
|
|
225
200
|
component,
|
|
226
201
|
offsetUpdater,
|
|
227
202
|
preferredFocus,
|
|
@@ -282,15 +257,22 @@ export class CellComponent extends React.Component<Props, State> {
|
|
|
282
257
|
style={styles.baseCell}
|
|
283
258
|
isFocusable={isFocusable}
|
|
284
259
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
285
|
-
{...this.accessibilityManager.getButtonAccessibilityProps(
|
|
286
|
-
item?.extensions?.accessibility?.label || item?.title
|
|
287
|
-
)}
|
|
288
260
|
>
|
|
289
261
|
{(focused, event) => {
|
|
290
262
|
const isFocused = this.isCellFocused(focused);
|
|
291
263
|
|
|
292
264
|
if (isFocused) {
|
|
293
|
-
|
|
265
|
+
const accessibilityManager = AccessibilityManager.getInstance();
|
|
266
|
+
|
|
267
|
+
const accessibilityTitle =
|
|
268
|
+
item?.extensions?.accessibility?.label || item?.title || "";
|
|
269
|
+
|
|
270
|
+
const accessibilityHint =
|
|
271
|
+
item?.extensions?.accessibility?.hint || "";
|
|
272
|
+
|
|
273
|
+
accessibilityManager.readText({
|
|
274
|
+
text: `${accessibilityTitle} ${accessibilityHint}`,
|
|
275
|
+
});
|
|
294
276
|
}
|
|
295
277
|
|
|
296
278
|
return (
|
|
@@ -5,7 +5,6 @@ import { BaseFocusable } from "../BaseFocusable";
|
|
|
5
5
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
6
6
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
7
7
|
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
|
|
8
|
-
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
9
8
|
|
|
10
9
|
type Props = {
|
|
11
10
|
initialFocus?: boolean;
|
|
@@ -29,7 +28,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
29
28
|
isGroup: boolean;
|
|
30
29
|
mouse: boolean;
|
|
31
30
|
longPressTimeout = null;
|
|
32
|
-
accessibilityManager: AccessibilityManager;
|
|
33
31
|
|
|
34
32
|
constructor(props) {
|
|
35
33
|
super(props);
|
|
@@ -44,8 +42,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
44
42
|
this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
|
|
45
43
|
this.longPress = this.longPress.bind(this);
|
|
46
44
|
this.press = this.press.bind(this);
|
|
47
|
-
|
|
48
|
-
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
/**
|
|
@@ -132,9 +128,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
132
128
|
const id = this.getId();
|
|
133
129
|
const focusableId = `focusable-${id}`;
|
|
134
130
|
|
|
135
|
-
const accessibilityProps =
|
|
136
|
-
this.accessibilityManager.getWebAccessibilityProps(this.props);
|
|
137
|
-
|
|
138
131
|
return (
|
|
139
132
|
<div
|
|
140
133
|
id={focusableId}
|
|
@@ -147,7 +140,6 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
147
140
|
data-testid={focusableId}
|
|
148
141
|
focused-teststate={focused ? "focused" : "default"}
|
|
149
142
|
style={style}
|
|
150
|
-
{...accessibilityProps}
|
|
151
143
|
>
|
|
152
144
|
{children(focused, { mouse: this.mouse })}
|
|
153
145
|
</div>
|
|
@@ -33,8 +33,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
33
33
|
focusedStyles,
|
|
34
34
|
pluginIdentifier,
|
|
35
35
|
preferredFocus,
|
|
36
|
-
index,
|
|
37
|
-
buttonsCount,
|
|
38
36
|
} = otherProps;
|
|
39
37
|
|
|
40
38
|
const focusableId = getFocusableId(prefixId, suffixId);
|
|
@@ -98,10 +96,8 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
98
96
|
});
|
|
99
97
|
|
|
100
98
|
if (ttsLabel) {
|
|
101
|
-
accessibilityManager.addHeading(ttsLabel);
|
|
102
|
-
|
|
103
99
|
accessibilityManager.readText({
|
|
104
|
-
text:
|
|
100
|
+
text: ttsLabel,
|
|
105
101
|
});
|
|
106
102
|
}
|
|
107
103
|
};
|
|
@@ -51,8 +51,6 @@ type Props = {
|
|
|
51
51
|
pluginIdentifier: string;
|
|
52
52
|
suffixId: string;
|
|
53
53
|
preferredFocus: boolean;
|
|
54
|
-
index: number;
|
|
55
|
-
buttonsCount: number;
|
|
56
54
|
};
|
|
57
55
|
|
|
58
56
|
export const Button = ({
|
|
@@ -62,8 +60,6 @@ export const Button = ({
|
|
|
62
60
|
pluginIdentifier,
|
|
63
61
|
suffixId,
|
|
64
62
|
preferredFocus,
|
|
65
|
-
index,
|
|
66
|
-
buttonsCount,
|
|
67
63
|
}: Props) => {
|
|
68
64
|
if (!value(`${suffixId}_button_enabled`)) return null;
|
|
69
65
|
|
|
@@ -132,8 +128,6 @@ export const Button = ({
|
|
|
132
128
|
},
|
|
133
129
|
pluginIdentifier,
|
|
134
130
|
preferredFocus,
|
|
135
|
-
index,
|
|
136
|
-
buttonsCount,
|
|
137
131
|
},
|
|
138
132
|
};
|
|
139
133
|
};
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { renderHook, act } from "@testing-library/react-hooks";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
import { useLoadingState } from "../useLoadingState";
|
|
4
|
+
|
|
5
|
+
// Mock the useRefWithInitialValue hook
|
|
6
|
+
jest.mock(
|
|
7
|
+
"@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue",
|
|
8
|
+
() => ({
|
|
9
|
+
useRefWithInitialValue: jest.fn((initializer) => ({
|
|
10
|
+
current: initializer(),
|
|
11
|
+
})),
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
describe("useLoadingState", () => {
|
|
16
|
+
let onLoadDone: jest.Mock;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
onLoadDone = jest.fn();
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("initialization", () => {
|
|
24
|
+
it("should initialize with correct default state for zero components", () => {
|
|
25
|
+
const { result } = renderHook(() => useLoadingState(0, onLoadDone));
|
|
26
|
+
|
|
27
|
+
const initialState = result.current.loadingState.getValue();
|
|
28
|
+
|
|
29
|
+
expect(initialState).toEqual({
|
|
30
|
+
index: -1,
|
|
31
|
+
done: true,
|
|
32
|
+
waitForAllComponents: false,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should initialize with correct default state for multiple components", () => {
|
|
39
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
40
|
+
|
|
41
|
+
const initialState = result.current.loadingState.getValue();
|
|
42
|
+
|
|
43
|
+
expect(initialState).toEqual({
|
|
44
|
+
index: -1,
|
|
45
|
+
done: false,
|
|
46
|
+
waitForAllComponents: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should return a BehaviorSubject for loadingState", () => {
|
|
53
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
54
|
+
|
|
55
|
+
expect(result.current.loadingState).toBeInstanceOf(BehaviorSubject);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("arePreviousComponentsLoaded", () => {
|
|
60
|
+
it("should return true for index 0 (first component)", () => {
|
|
61
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
62
|
+
|
|
63
|
+
expect(result.current.arePreviousComponentsLoaded(0)).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should return false when previous components are not loaded", () => {
|
|
67
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
68
|
+
|
|
69
|
+
expect(result.current.arePreviousComponentsLoaded(1)).toBe(false);
|
|
70
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should return true when all previous components are loaded", () => {
|
|
74
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
75
|
+
|
|
76
|
+
act(() => {
|
|
77
|
+
result.current.onLoadFinished(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(result.current.arePreviousComponentsLoaded(1)).toBe(true);
|
|
81
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(false);
|
|
82
|
+
|
|
83
|
+
act(() => {
|
|
84
|
+
result.current.onLoadFinished(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("onLoadFinished", () => {
|
|
92
|
+
it("should update component state and loading state when component finishes loading", () => {
|
|
93
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
94
|
+
|
|
95
|
+
act(() => {
|
|
96
|
+
result.current.onLoadFinished(0);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const state = result.current.loadingState.getValue();
|
|
100
|
+
expect(state.index).toBe(0);
|
|
101
|
+
expect(state.done).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should update index to highest loaded component", () => {
|
|
105
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
106
|
+
|
|
107
|
+
act(() => {
|
|
108
|
+
result.current.onLoadFinished(2);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
let state = result.current.loadingState.getValue();
|
|
112
|
+
expect(state.index).toBe(2);
|
|
113
|
+
|
|
114
|
+
act(() => {
|
|
115
|
+
result.current.onLoadFinished(1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
state = result.current.loadingState.getValue();
|
|
119
|
+
expect(state.index).toBe(2); // Should remain 2, not decrease to 1
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should mark as done when all components are loaded", () => {
|
|
123
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
124
|
+
|
|
125
|
+
act(() => {
|
|
126
|
+
result.current.onLoadFinished(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
let state = result.current.loadingState.getValue();
|
|
130
|
+
expect(state.done).toBe(true); // True because arePreviousComponentsLoaded(1) returns true when component 0 is loaded
|
|
131
|
+
expect(onLoadDone).toHaveBeenCalledTimes(1);
|
|
132
|
+
|
|
133
|
+
act(() => {
|
|
134
|
+
result.current.onLoadFinished(1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
state = result.current.loadingState.getValue();
|
|
138
|
+
expect(state.done).toBe(true);
|
|
139
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called again
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should call onLoadDone when count is 0", () => {
|
|
143
|
+
const { result } = renderHook(() => useLoadingState(0, onLoadDone));
|
|
144
|
+
|
|
145
|
+
const state = result.current.loadingState.getValue();
|
|
146
|
+
expect(state.done).toBe(true);
|
|
147
|
+
// onLoadDone is not called on initialization for count 0, only when all components are loaded via dispatch
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should handle loading components out of order", () => {
|
|
151
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
152
|
+
|
|
153
|
+
// Load component 2 first
|
|
154
|
+
act(() => {
|
|
155
|
+
result.current.onLoadFinished(2);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
let state = result.current.loadingState.getValue();
|
|
159
|
+
expect(state.done).toBe(false);
|
|
160
|
+
|
|
161
|
+
// Load component 0
|
|
162
|
+
act(() => {
|
|
163
|
+
result.current.onLoadFinished(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
state = result.current.loadingState.getValue();
|
|
167
|
+
expect(state.done).toBe(false);
|
|
168
|
+
|
|
169
|
+
// Load component 1 - should complete loading
|
|
170
|
+
act(() => {
|
|
171
|
+
result.current.onLoadFinished(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
state = result.current.loadingState.getValue();
|
|
175
|
+
expect(state.done).toBe(true);
|
|
176
|
+
expect(onLoadDone).toHaveBeenCalledTimes(1);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should call onLoadDone again on subsequent dispatches", () => {
|
|
180
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
181
|
+
|
|
182
|
+
act(() => {
|
|
183
|
+
result.current.onLoadFinished(0);
|
|
184
|
+
result.current.onLoadFinished(1);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called for each dispatch when done is true
|
|
188
|
+
|
|
189
|
+
// Try loading again - onLoadDone will be called again because dispatch runs again
|
|
190
|
+
act(() => {
|
|
191
|
+
result.current.onLoadFinished(0);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(onLoadDone).toHaveBeenCalledTimes(3); // Will be called again
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("onLoadFailed", () => {
|
|
199
|
+
it("should treat failed components as loaded when SHOULD_FAIL_ON_COMPONENT_LOADING is false", () => {
|
|
200
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
201
|
+
|
|
202
|
+
const error = new Error("Load failed");
|
|
203
|
+
|
|
204
|
+
act(() => {
|
|
205
|
+
result.current.onLoadFailed({ error, index: 0 });
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const state = result.current.loadingState.getValue();
|
|
209
|
+
expect(state.index).toBe(0);
|
|
210
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should complete loading when all components fail", () => {
|
|
214
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
215
|
+
|
|
216
|
+
const error = new Error("Load failed");
|
|
217
|
+
|
|
218
|
+
act(() => {
|
|
219
|
+
result.current.onLoadFailed({ error, index: 0 });
|
|
220
|
+
result.current.onLoadFailed({ error, index: 1 });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const state = result.current.loadingState.getValue();
|
|
224
|
+
expect(state.done).toBe(true);
|
|
225
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called for each failed component
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("should handle mixed success and failure", () => {
|
|
229
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
230
|
+
|
|
231
|
+
const error = new Error("Load failed");
|
|
232
|
+
|
|
233
|
+
act(() => {
|
|
234
|
+
result.current.onLoadFinished(0);
|
|
235
|
+
result.current.onLoadFailed({ error, index: 1 });
|
|
236
|
+
result.current.onLoadFinished(2);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const state = result.current.loadingState.getValue();
|
|
240
|
+
expect(state.done).toBe(true);
|
|
241
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called when all components 0,1,2 are handled
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("loading state observable", () => {
|
|
246
|
+
it("should emit state changes through BehaviorSubject", () => {
|
|
247
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone)); // Use 3 components so loading component 0 doesn't complete everything
|
|
248
|
+
const mockSubscriber = jest.fn();
|
|
249
|
+
|
|
250
|
+
result.current.loadingState.subscribe(mockSubscriber);
|
|
251
|
+
|
|
252
|
+
act(() => {
|
|
253
|
+
result.current.onLoadFinished(0);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Should have been called twice: initial state + update
|
|
257
|
+
expect(mockSubscriber).toHaveBeenCalledTimes(2);
|
|
258
|
+
|
|
259
|
+
expect(mockSubscriber).toHaveBeenLastCalledWith({
|
|
260
|
+
index: 0,
|
|
261
|
+
done: false, // Will be false because we need components 1 and 2 as well
|
|
262
|
+
waitForAllComponents: false,
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should preserve waitForAllComponents flag in state updates", () => {
|
|
267
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
268
|
+
|
|
269
|
+
act(() => {
|
|
270
|
+
result.current.onLoadFinished(0);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const state = result.current.loadingState.getValue();
|
|
274
|
+
expect(state.waitForAllComponents).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe("memoization", () => {
|
|
279
|
+
it("should return stable references for functions", () => {
|
|
280
|
+
const { result, rerender } = renderHook(() =>
|
|
281
|
+
useLoadingState(2, onLoadDone)
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
const firstRender = {
|
|
285
|
+
onLoadFinished: result.current.onLoadFinished,
|
|
286
|
+
onLoadFailed: result.current.onLoadFailed,
|
|
287
|
+
arePreviousComponentsLoaded: result.current.arePreviousComponentsLoaded,
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
rerender();
|
|
291
|
+
|
|
292
|
+
expect(result.current.onLoadFinished).toBe(firstRender.onLoadFinished);
|
|
293
|
+
expect(result.current.onLoadFailed).toBe(firstRender.onLoadFailed);
|
|
294
|
+
|
|
295
|
+
expect(result.current.arePreviousComponentsLoaded).toBe(
|
|
296
|
+
firstRender.arePreviousComponentsLoaded
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should return stable function references (current behavior)", () => {
|
|
301
|
+
const { result, rerender } = renderHook(
|
|
302
|
+
({ onLoadDone }) => useLoadingState(2, onLoadDone),
|
|
303
|
+
{ initialProps: { onLoadDone } }
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
const firstResult = result.current;
|
|
307
|
+
|
|
308
|
+
const newOnLoadDone = jest.fn();
|
|
309
|
+
rerender({ onLoadDone: newOnLoadDone });
|
|
310
|
+
|
|
311
|
+
// Functions should remain the same due to empty dependency arrays (this is the current behavior)
|
|
312
|
+
expect(result.current.onLoadFinished).toBe(firstResult.onLoadFinished);
|
|
313
|
+
expect(result.current.onLoadFailed).toBe(firstResult.onLoadFailed);
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe("edge cases", () => {
|
|
318
|
+
it("should handle duplicate load finished calls gracefully", () => {
|
|
319
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
320
|
+
|
|
321
|
+
act(() => {
|
|
322
|
+
result.current.onLoadFinished(0);
|
|
323
|
+
result.current.onLoadFinished(0); // Duplicate call
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const state = result.current.loadingState.getValue();
|
|
327
|
+
expect(state.index).toBe(0);
|
|
328
|
+
expect(state.done).toBe(true); // True because loading component 0 makes it done in a 2-component setup
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should handle loading index greater than component count", () => {
|
|
332
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
333
|
+
|
|
334
|
+
act(() => {
|
|
335
|
+
result.current.onLoadFinished(5); // Index out of bounds
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const state = result.current.loadingState.getValue();
|
|
339
|
+
expect(state.index).toBe(5);
|
|
340
|
+
expect(state.done).toBe(false); // Should not be done as not all components loaded
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should handle negative indices", () => {
|
|
344
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
345
|
+
|
|
346
|
+
act(() => {
|
|
347
|
+
result.current.onLoadFinished(-1);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const state = result.current.loadingState.getValue();
|
|
351
|
+
expect(state.index).toBe(-1); // Should remain -1
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe("component count changes", () => {
|
|
356
|
+
it("should handle changing component count", () => {
|
|
357
|
+
const { result, rerender } = renderHook(
|
|
358
|
+
({ count }) => useLoadingState(count, onLoadDone),
|
|
359
|
+
{ initialProps: { count: 2 } }
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
act(() => {
|
|
363
|
+
result.current.onLoadFinished(0);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Change count
|
|
367
|
+
rerender({ count: 3 });
|
|
368
|
+
|
|
369
|
+
// The hook should work with the new count
|
|
370
|
+
act(() => {
|
|
371
|
+
result.current.onLoadFinished(1);
|
|
372
|
+
result.current.onLoadFinished(2);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const state = result.current.loadingState.getValue();
|
|
376
|
+
expect(state.done).toBe(true);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { isNil, set, lensIndex,
|
|
2
|
+
import { isNil, set, lensIndex, slice } from "ramda";
|
|
3
3
|
import { BehaviorSubject } from "rxjs";
|
|
4
4
|
import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
|
|
5
5
|
|
|
@@ -53,7 +53,7 @@ export const useLoadingState = (
|
|
|
53
53
|
|
|
54
54
|
const componentsBefore = slice(0, index, componentStateRef.current);
|
|
55
55
|
|
|
56
|
-
return componentsBefore.every(
|
|
56
|
+
return componentsBefore.every(Boolean);
|
|
57
57
|
}, []);
|
|
58
58
|
|
|
59
59
|
const dispatch = React.useCallback(({ payload }) => {
|
|
@@ -2,19 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`<TextInputTv /> renders 1`] = `
|
|
4
4
|
<input
|
|
5
|
-
accessibilityProps={
|
|
6
|
-
{
|
|
7
|
-
"accessibilityHint": "Enter text into Search",
|
|
8
|
-
"accessibilityLabel": "Search",
|
|
9
|
-
"accessibilityRole": "text",
|
|
10
|
-
"accessible": true,
|
|
11
|
-
"aria-description": "Enter text into Search",
|
|
12
|
-
"aria-label": "Search",
|
|
13
|
-
"aria-role": "text",
|
|
14
|
-
"role": "text",
|
|
15
|
-
"tabindex": 0,
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
5
|
testID="TextInput-tv"
|
|
19
6
|
/>
|
|
20
7
|
`;
|
|
@@ -4,7 +4,6 @@ import { Appearance, Platform, StyleSheet, TextInput } from "react-native";
|
|
|
4
4
|
import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
5
5
|
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
6
6
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
7
|
-
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
8
7
|
|
|
9
8
|
type Props = Partial<{
|
|
10
9
|
style: any;
|
|
@@ -43,8 +42,6 @@ function TextInputTV(props: Props, ref) {
|
|
|
43
42
|
const [colorScheme, setColorScheme] = useState(getInitialColorScheme());
|
|
44
43
|
const isRTL = useIsRTL();
|
|
45
44
|
|
|
46
|
-
const accessibilityManager = useAccessibilityManager({});
|
|
47
|
-
|
|
48
45
|
const onColorChange = useCallback(
|
|
49
46
|
({ colorScheme: color }) => {
|
|
50
47
|
if (color !== colorScheme) {
|
|
@@ -156,13 +153,6 @@ function TextInputTV(props: Props, ref) {
|
|
|
156
153
|
])
|
|
157
154
|
)(props);
|
|
158
155
|
|
|
159
|
-
const getAccessibilityProps = () => {
|
|
160
|
-
return {
|
|
161
|
-
accessibilityProps:
|
|
162
|
-
accessibilityManager.getInputAccessibilityProps("Search"),
|
|
163
|
-
};
|
|
164
|
-
};
|
|
165
|
-
|
|
166
156
|
const inputProps = {
|
|
167
157
|
...getProps(),
|
|
168
158
|
...getStyle(),
|
|
@@ -171,7 +161,6 @@ function TextInputTV(props: Props, ref) {
|
|
|
171
161
|
...getSecureTextEntry(),
|
|
172
162
|
...getOnEndEditing(),
|
|
173
163
|
...getOnPress(),
|
|
174
|
-
...getAccessibilityProps(),
|
|
175
164
|
};
|
|
176
165
|
|
|
177
166
|
if (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "13.0.7
|
|
3
|
+
"version": "13.0.7",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"redux-mock-store": "^1.5.3"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@applicaster/applicaster-types": "13.0.7
|
|
35
|
-
"@applicaster/zapp-react-native-bridge": "13.0.7
|
|
36
|
-
"@applicaster/zapp-react-native-redux": "13.0.7
|
|
37
|
-
"@applicaster/zapp-react-native-utils": "13.0.7
|
|
34
|
+
"@applicaster/applicaster-types": "13.0.7",
|
|
35
|
+
"@applicaster/zapp-react-native-bridge": "13.0.7",
|
|
36
|
+
"@applicaster/zapp-react-native-redux": "13.0.7",
|
|
37
|
+
"@applicaster/zapp-react-native-utils": "13.0.7",
|
|
38
38
|
"promise": "^8.3.0",
|
|
39
39
|
"react-router-native": "^5.1.2",
|
|
40
40
|
"url": "^0.11.0",
|