@applicaster/zapp-react-native-utils 16.0.0-alpha.3081324238 → 16.0.0-alpha.3202041045
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/actionUtils/index.ts +3 -1
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +139 -5
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +24 -0
- package/actionsExecutor/actions/navigateToScreen.ts +29 -45
- package/actionsExecutor/actions/openBottomSheet.ts +1 -0
- package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerToolbox.test.ts +188 -4
- package/appUtils/playerManager/index.ts +5 -0
- package/appUtils/playerManager/player.ts +15 -0
- package/appUtils/playerManager/playerToolbox.ts +45 -41
- package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
- package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
- package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
- package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
- package/cellUtils/index.ts +40 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +60 -0
- package/focusManager/FocusManager.ts +26 -0
- package/focusManager/__tests__/FocusManager.test.ts +9 -0
- package/localizationUtils/index.ts +12 -6
- package/logger/__tests__/xrayToolbox.test.ts +69 -0
- package/logger/xrayToolbox.ts +67 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/keys.js +7 -1
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
- package/manifestUtils/mobileAction/groups/defaults.js +1 -0
- package/navigationUtils/xrayNavigator.ts +12 -0
- package/package.json +3 -2
- package/quickBrickAssets/__tests__/index.test.ts +18 -0
- package/quickBrickAssets/index.ts +35 -0
- package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
- package/reactHooks/actions/index.ts +29 -23
- package/reactHooks/usePluginConfiguration.ts +84 -46
- package/riverComponetsMeasurementProvider/index.tsx +3 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app navigator for the X-Ray toolboxes (nav, screen), which run outside React. The
|
|
3
|
+
* NavigationProvider sets it after each commit; it is a plain module variable, so updating it
|
|
4
|
+
* never renders anything.
|
|
5
|
+
*/
|
|
6
|
+
let navigator: QuickBrickAppNavigator | undefined;
|
|
7
|
+
|
|
8
|
+
export const setXrayNavigator = (value: QuickBrickAppNavigator | undefined) => {
|
|
9
|
+
navigator = value;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getXrayNavigator = () => navigator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "16.0.0-alpha.
|
|
3
|
+
"version": "16.0.0-alpha.3202041045",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "16.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "16.0.0-alpha.3202041045",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"prop-types": "^15.0.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
+
"@applicaster/quick-brick-assets": "*",
|
|
39
40
|
"@applicaster/zapp-pipes-v2-client": "*",
|
|
40
41
|
"@react-native-community/netinfo": "*",
|
|
41
42
|
"react": "*",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// The Jest stand-in for @applicaster/quick-brick-assets maps each existing key to itself
|
|
2
|
+
import { getQuickBrickAsset } from "..";
|
|
3
|
+
|
|
4
|
+
describe("getQuickBrickAsset", () => {
|
|
5
|
+
it("returns the asset for a plugin identifier and path", () => {
|
|
6
|
+
expect(
|
|
7
|
+
getQuickBrickAsset("mute_unmute_action", "action/flavour_1/mute.png")
|
|
8
|
+
).toBe("mute_unmute_action/action/flavour_1/mute.png");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("rejects unknown paths and plugin identifiers at compile time", () => {
|
|
12
|
+
// @ts-expect-error - no such file in mute_unmute_action
|
|
13
|
+
getQuickBrickAsset("mute_unmute_action", "action/flavour_9/mute.png");
|
|
14
|
+
|
|
15
|
+
// @ts-expect-error - no assets folder for this plugin
|
|
16
|
+
getQuickBrickAsset("unknown_plugin", "action/flavour_1/mute.png");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assets from "@applicaster/quick-brick-assets";
|
|
2
|
+
import type {
|
|
3
|
+
QuickBrickAssetKey,
|
|
4
|
+
QuickBrickAssetSource,
|
|
5
|
+
} from "@applicaster/quick-brick-assets";
|
|
6
|
+
|
|
7
|
+
export type { QuickBrickAssetKey, QuickBrickAssetSource };
|
|
8
|
+
|
|
9
|
+
type PluginOf<K> = K extends `${infer Plugin}/${string}` ? Plugin : never;
|
|
10
|
+
|
|
11
|
+
type PathOf<K, Plugin extends string> = K extends `${Plugin}/${infer Path}`
|
|
12
|
+
? Path
|
|
13
|
+
: never;
|
|
14
|
+
|
|
15
|
+
/** Plugin identifiers that have a folder in `@applicaster/quick-brick-assets`. */
|
|
16
|
+
export type QuickBrickAssetPlugin = PluginOf<QuickBrickAssetKey>;
|
|
17
|
+
|
|
18
|
+
/** Asset paths inside the given plugin's folder. */
|
|
19
|
+
export type QuickBrickAssetPath<Plugin extends QuickBrickAssetPlugin> = PathOf<
|
|
20
|
+
QuickBrickAssetKey,
|
|
21
|
+
Plugin
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Looks up an asset of `@applicaster/quick-brick-assets` by plugin identifier
|
|
26
|
+
* and path relative to the plugin folder, e.g.
|
|
27
|
+
* `getQuickBrickAsset("mute_unmute_action", "action/flavour_1/mute.png")`.
|
|
28
|
+
* Both arguments are checked against the package's files at compile time.
|
|
29
|
+
* Density variants (`@2x` / `@3x`) are not supported.
|
|
30
|
+
*/
|
|
31
|
+
export const getQuickBrickAsset = <Plugin extends QuickBrickAssetPlugin>(
|
|
32
|
+
pluginIdentifier: Plugin,
|
|
33
|
+
assetPath: QuickBrickAssetPath<Plugin>
|
|
34
|
+
): QuickBrickAssetSource =>
|
|
35
|
+
assets[`${pluginIdentifier}/${assetPath}` as QuickBrickAssetKey];
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-native";
|
|
2
|
+
|
|
3
|
+
const plugin = {
|
|
4
|
+
identifier: "cast",
|
|
5
|
+
configuration: {
|
|
6
|
+
title: "Configured",
|
|
7
|
+
localizations: { fr: { close: "Fermer" } },
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => ({
|
|
12
|
+
usePlugins: () => [plugin],
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock(
|
|
16
|
+
"@applicaster/zapp-react-native-utils/appUtils/localizationsHelper",
|
|
17
|
+
() => ({ getUILanguage: () => "fr" })
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
getPluginConfiguration,
|
|
22
|
+
mergeLocalizations,
|
|
23
|
+
usePluginConfiguration,
|
|
24
|
+
} from "../usePluginConfiguration";
|
|
25
|
+
|
|
26
|
+
const manifest = {
|
|
27
|
+
custom_configuration_fields: [
|
|
28
|
+
{ key: "title", initial_value: "Default" },
|
|
29
|
+
{ key: "color", initial_value: "red" },
|
|
30
|
+
],
|
|
31
|
+
localizations: {
|
|
32
|
+
fields: [
|
|
33
|
+
{ key: "close", initial_value: "Close" },
|
|
34
|
+
{ key: "cancel", initial_value: "Cancel" },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
it("matches usePluginConfiguration outside React", () => {
|
|
40
|
+
const { result } = renderHook(() => usePluginConfiguration("cast", manifest));
|
|
41
|
+
|
|
42
|
+
expect(getPluginConfiguration(plugin, manifest)).toEqual(result.current);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Expected values are what R.mergeDeepWith((l, r) => (isNil(l) ? r : l)) returned before ramda was removed
|
|
46
|
+
describe("mergeLocalizations", () => {
|
|
47
|
+
const defaults = { en: { close: "Close", cancel: "Cancel" } };
|
|
48
|
+
|
|
49
|
+
it("returns the defaults when nothing is configured", () => {
|
|
50
|
+
expect(mergeLocalizations(undefined, defaults)).toEqual(defaults);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("keeps configured values, fills nil ones and missing keys from defaults", () => {
|
|
54
|
+
expect(
|
|
55
|
+
mergeLocalizations(
|
|
56
|
+
{ en: { close: null, extra: "X" }, fr: { close: "Fermer" } },
|
|
57
|
+
defaults
|
|
58
|
+
)
|
|
59
|
+
).toEqual({
|
|
60
|
+
en: { close: "Close", extra: "X", cancel: "Cancel" },
|
|
61
|
+
fr: { close: "Fermer" },
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("does not merge arrays", () => {
|
|
66
|
+
expect(
|
|
67
|
+
mergeLocalizations(
|
|
68
|
+
{ en: { close: ["a"] } },
|
|
69
|
+
{ en: { close: ["b"], cancel: "C" } }
|
|
70
|
+
)
|
|
71
|
+
).toEqual({ en: { close: ["a"], cancel: "C" } });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("keeps configured languages first, so the fallback language stays the configured one", () => {
|
|
76
|
+
const merged = mergeLocalizations(
|
|
77
|
+
{ he: { close: "סגור" }, en: { close: "Close it" } },
|
|
78
|
+
{ en: { close: "Close" } }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(Object.keys(merged)).toEqual(["he", "en"]);
|
|
82
|
+
});
|
|
@@ -21,17 +21,45 @@ type ActionContexts<T = unknown> = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* A value that changes whenever a provider is added to or removed from
|
|
25
|
+
* `uiActionsRegistry`.
|
|
26
|
+
*
|
|
27
|
+
* Providers can register after the UI that wants them has rendered - the audio
|
|
28
|
+
* player registers playback speed only once its controller has state, and
|
|
29
|
+
* plugin-backed actions are bridged in from an effect. Put this in the
|
|
30
|
+
* dependency list of anything that resolves actions, so the list is re-read
|
|
31
|
+
* instead of being frozen at first paint.
|
|
32
|
+
*/
|
|
33
|
+
export function useRegistryRevision(): number {
|
|
34
|
+
const [revision, setRevision] = useState(0);
|
|
35
|
+
|
|
36
|
+
useEffect(
|
|
37
|
+
() =>
|
|
38
|
+
uiActionsRegistry.subscribe(() => setRevision((current) => current + 1)),
|
|
39
|
+
[]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return revision;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves a legacy action context or falls back to the UI action registry.
|
|
25
47
|
* @param {*} plugId - subscribes to the context of that action plugin
|
|
26
48
|
* @return {*} - global action provider context or context for specific action plugin
|
|
27
49
|
*/
|
|
28
50
|
export function useActions<T = unknown>(plugId: string): any {
|
|
29
51
|
const [logged, setLogged] = useState(false);
|
|
30
52
|
const context = useContext<ActionContexts<T>>(ActionsContext);
|
|
53
|
+
useRegistryRevision();
|
|
54
|
+
|
|
31
55
|
const actionContext = context?.actions?.[plugId]?.module?.context;
|
|
32
56
|
|
|
33
57
|
if (plugId) {
|
|
34
58
|
if (!actionContext) {
|
|
59
|
+
const registeredAction = uiActionsRegistry.getAction(plugId);
|
|
60
|
+
|
|
61
|
+
if (registeredAction) return registeredAction;
|
|
62
|
+
|
|
35
63
|
if (!logged) {
|
|
36
64
|
logger.warning({
|
|
37
65
|
message: `useActions: Couldn't find an action for ${plugId} plugin`,
|
|
@@ -139,25 +167,3 @@ export function useEntryActionState(
|
|
|
139
167
|
|
|
140
168
|
return { state, invokeAction };
|
|
141
169
|
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* A value that changes whenever a provider is added to or removed from
|
|
145
|
-
* `uiActionsRegistry`.
|
|
146
|
-
*
|
|
147
|
-
* Providers can register after the UI that wants them has rendered - the audio
|
|
148
|
-
* player registers playback speed only once its controller has state, and
|
|
149
|
-
* plugin-backed actions are bridged in from an effect. Put this in the
|
|
150
|
-
* dependency list of anything that resolves actions, so the list is re-read
|
|
151
|
-
* instead of being frozen at first paint.
|
|
152
|
-
*/
|
|
153
|
-
export function useRegistryRevision(): number {
|
|
154
|
-
const [revision, setRevision] = useState(0);
|
|
155
|
-
|
|
156
|
-
useEffect(
|
|
157
|
-
() =>
|
|
158
|
-
uiActionsRegistry.subscribe(() => setRevision((current) => current + 1)),
|
|
159
|
-
[]
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return revision;
|
|
163
|
-
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
1
|
import { useMemo } from "react";
|
|
3
2
|
|
|
4
3
|
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
@@ -6,7 +5,8 @@ import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
6
5
|
// importing this way prevents typescript packages importing from this
|
|
7
6
|
// file to go crazy reporting errors on JS
|
|
8
7
|
import { populateConfigurationValues } from "@applicaster/zapp-react-native-utils/configurationUtils";
|
|
9
|
-
import { useLocalizedStrings } from "../localizationUtils";
|
|
8
|
+
import { getLocalizedStrings, useLocalizedStrings } from "../localizationUtils";
|
|
9
|
+
import { isNil, pathOr } from "../utils";
|
|
10
10
|
|
|
11
11
|
const populateFields = (
|
|
12
12
|
data,
|
|
@@ -14,16 +14,72 @@ const populateFields = (
|
|
|
14
14
|
pathToValues: string | string[] = "configuration",
|
|
15
15
|
skipDefaults = false
|
|
16
16
|
) =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
17
|
+
populateConfigurationValues(
|
|
18
|
+
manifestFields,
|
|
19
|
+
pathOr({}, pathToValues, data),
|
|
20
|
+
skipDefaults
|
|
21
|
+
);
|
|
22
22
|
|
|
23
23
|
export function parsePluginConfiguration(plugin, manifest) {
|
|
24
24
|
return populateFields(plugin, manifest.custom_configuration_fields);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const getDefaultLocalizations = (manifest) =>
|
|
28
|
+
manifest?.localizations?.fields?.reduce(
|
|
29
|
+
(defaults, { key, initial_value }) => ({
|
|
30
|
+
en: { ...defaults.en, [key]: initial_value },
|
|
31
|
+
}),
|
|
32
|
+
{ en: {} }
|
|
33
|
+
) || {};
|
|
34
|
+
|
|
35
|
+
const isPlainObject = (value) =>
|
|
36
|
+
Object.prototype.toString.call(value) === "[object Object]";
|
|
37
|
+
|
|
38
|
+
// Deep merge where configured values win, except nil ones (defaults fill the gaps)
|
|
39
|
+
export const mergeLocalizations = (configured, defaults) => {
|
|
40
|
+
if (!isPlainObject(configured)) return { ...defaults };
|
|
41
|
+
if (!isPlainObject(defaults)) return { ...configured };
|
|
42
|
+
|
|
43
|
+
// Configured keys first, as R.mergeDeepWith: the first language is the fallback
|
|
44
|
+
return Object.keys({ ...configured, ...defaults }).reduce((merged, key) => {
|
|
45
|
+
const left = configured[key];
|
|
46
|
+
const right = defaults[key];
|
|
47
|
+
|
|
48
|
+
if (!(key in defaults)) {
|
|
49
|
+
merged[key] = left;
|
|
50
|
+
} else if (!(key in configured)) {
|
|
51
|
+
merged[key] = right;
|
|
52
|
+
} else if (isPlainObject(left) && isPlainObject(right)) {
|
|
53
|
+
merged[key] = mergeLocalizations(left, right);
|
|
54
|
+
} else {
|
|
55
|
+
merged[key] = isNil(left) ? right : left;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return merged;
|
|
59
|
+
}, {});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* usePluginConfiguration outside React: the same result, read once instead of subscribed.
|
|
64
|
+
*/
|
|
65
|
+
export function getPluginConfiguration<T = any, S = Record<string, any>>(
|
|
66
|
+
plugin,
|
|
67
|
+
manifest?: any
|
|
68
|
+
): { configurationFields: T; localizations: S } {
|
|
69
|
+
return {
|
|
70
|
+
configurationFields: populateFields(
|
|
71
|
+
plugin,
|
|
72
|
+
manifest?.custom_configuration_fields
|
|
73
|
+
),
|
|
74
|
+
localizations: getLocalizedStrings(
|
|
75
|
+
mergeLocalizations(
|
|
76
|
+
plugin?.configuration?.localizations,
|
|
77
|
+
getDefaultLocalizations(manifest)
|
|
78
|
+
)
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
27
83
|
export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
28
84
|
identifier: string,
|
|
29
85
|
manifest?: any
|
|
@@ -31,7 +87,7 @@ export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
|
31
87
|
const plugins = usePlugins();
|
|
32
88
|
|
|
33
89
|
const plugin = useMemo(
|
|
34
|
-
() =>
|
|
90
|
+
() => plugins?.find((item) => item?.identifier === identifier),
|
|
35
91
|
[plugins, identifier]
|
|
36
92
|
);
|
|
37
93
|
|
|
@@ -40,29 +96,20 @@ export function usePluginConfiguration<T = any, S = Record<string, any>>(
|
|
|
40
96
|
[plugin, manifest]
|
|
41
97
|
);
|
|
42
98
|
|
|
43
|
-
const defaultLocalizations = useMemo(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
R.assocPath(["en", key], initial_value, defaults),
|
|
48
|
-
{ en: {} }
|
|
49
|
-
) || {}
|
|
50
|
-
);
|
|
51
|
-
}, [manifest]);
|
|
52
|
-
|
|
53
|
-
const localizationsParams = useMemo(() => {
|
|
54
|
-
return {
|
|
55
|
-
localizations: R.mergeDeepWith(
|
|
56
|
-
(left, right) => {
|
|
57
|
-
if (R.isNil(left)) return right;
|
|
99
|
+
const defaultLocalizations = useMemo(
|
|
100
|
+
() => getDefaultLocalizations(manifest),
|
|
101
|
+
[manifest]
|
|
102
|
+
);
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
const localizationsParams = useMemo(
|
|
105
|
+
() => ({
|
|
106
|
+
localizations: mergeLocalizations(
|
|
61
107
|
plugin?.configuration?.localizations,
|
|
62
108
|
defaultLocalizations
|
|
63
109
|
),
|
|
64
|
-
}
|
|
65
|
-
|
|
110
|
+
}),
|
|
111
|
+
[plugin?.configuration?.localizations, defaultLocalizations]
|
|
112
|
+
);
|
|
66
113
|
|
|
67
114
|
const localizations: S = useLocalizedStrings(localizationsParams);
|
|
68
115
|
|
|
@@ -105,29 +152,20 @@ export function useLayoutConfiguration<
|
|
|
105
152
|
"localizations",
|
|
106
153
|
];
|
|
107
154
|
|
|
108
|
-
const defaultLocalizations = useMemo(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
R.assocPath(["en", key], initial_value, defaults),
|
|
113
|
-
{ en: {} }
|
|
114
|
-
) || {}
|
|
115
|
-
);
|
|
116
|
-
}, [manifest]);
|
|
117
|
-
|
|
118
|
-
const localizationsParams = useMemo(() => {
|
|
119
|
-
return {
|
|
120
|
-
localizations: R.mergeDeepWith(
|
|
121
|
-
(left, right) => {
|
|
122
|
-
if (R.isNil(left)) return right;
|
|
155
|
+
const defaultLocalizations = useMemo(
|
|
156
|
+
() => getDefaultLocalizations(manifest),
|
|
157
|
+
[manifest]
|
|
158
|
+
);
|
|
123
159
|
|
|
124
|
-
|
|
125
|
-
|
|
160
|
+
const localizationsParams = useMemo(
|
|
161
|
+
() => ({
|
|
162
|
+
localizations: mergeLocalizations(
|
|
126
163
|
layoutData?.localizations,
|
|
127
164
|
defaultLocalizations
|
|
128
165
|
),
|
|
129
|
-
}
|
|
130
|
-
|
|
166
|
+
}),
|
|
167
|
+
[layoutData?.localizations, defaultLocalizations]
|
|
168
|
+
);
|
|
131
169
|
|
|
132
170
|
// Always call useLocalizedStrings regardless of whether localizations is in the keys
|
|
133
171
|
const localizations: L = useLocalizedStrings(localizationsParams);
|
|
@@ -34,6 +34,7 @@ type Props = {
|
|
|
34
34
|
type ItemProps = {
|
|
35
35
|
children: React.ReactNode;
|
|
36
36
|
index: number;
|
|
37
|
+
testID?: string;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
type MeasurementContext = {
|
|
@@ -153,11 +154,12 @@ export const ScreenLoadingMeasurements = ({
|
|
|
153
154
|
export const ScreenLoadingMeasurementsListItemWrapper = ({
|
|
154
155
|
children,
|
|
155
156
|
index,
|
|
157
|
+
testID,
|
|
156
158
|
}: ItemProps) => {
|
|
157
159
|
const { onLayout } = React.useContext(MeasurementsSettersContext);
|
|
158
160
|
|
|
159
161
|
return (
|
|
160
|
-
<View style={styles.container} onLayout={onLayout?.(index)}>
|
|
162
|
+
<View style={styles.container} onLayout={onLayout?.(index)} testID={testID}>
|
|
161
163
|
{children}
|
|
162
164
|
</View>
|
|
163
165
|
);
|