@applicaster/zapp-react-native-ui-components 16.0.0-rc.118 → 16.0.0-rc.119
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.
|
@@ -76,6 +76,66 @@ describe("masterCellBuilder", () => {
|
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
+
describe("cellOptions.interactive", () => {
|
|
80
|
+
// renders a single View tagged with the state the cell was built with
|
|
81
|
+
const dataAdapter =
|
|
82
|
+
() =>
|
|
83
|
+
({ state }) => [
|
|
84
|
+
{ type: "View", style: {}, props: { testID: `state-${state}` } },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Only the root View gets this testID: cell elements pass their own testID, which overrides it
|
|
88
|
+
const ROOT_TEST_ID = "master-cell-root";
|
|
89
|
+
|
|
90
|
+
// forwardRef: MasterCell attaches a ref to its root View
|
|
91
|
+
const TestView = React.forwardRef((props, ref) => (
|
|
92
|
+
<View testID={ROOT_TEST_ID} {...props} ref={ref} />
|
|
93
|
+
));
|
|
94
|
+
|
|
95
|
+
const renderCell = (cellOptions, state) => {
|
|
96
|
+
const MasterCell = masterCellBuilder({
|
|
97
|
+
cellConfiguration: singleView,
|
|
98
|
+
dataAdapter,
|
|
99
|
+
components: { View: TestView },
|
|
100
|
+
cellOptions,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return renderWithProviders(<MasterCell item={entry} state={state} />);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const getRoot = (wrapper) => wrapper.getByTestId(ROOT_TEST_ID);
|
|
107
|
+
|
|
108
|
+
it("keeps focused state and default root props when omitted", () => {
|
|
109
|
+
const wrapper = renderCell(undefined, "focused");
|
|
110
|
+
const root = getRoot(wrapper);
|
|
111
|
+
|
|
112
|
+
expect(wrapper.getByTestId("state-focused")).toBeDefined();
|
|
113
|
+
expect(root.props.onStartShouldSetResponder).toBeUndefined();
|
|
114
|
+
expect(root.props.collapsable).toBeUndefined();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("claims body touches on a non-collapsable root when false", () => {
|
|
118
|
+
const wrapper = renderCell({ interactive: false }, "default");
|
|
119
|
+
const root = getRoot(wrapper);
|
|
120
|
+
|
|
121
|
+
expect(root.props.onStartShouldSetResponder()).toBe(true);
|
|
122
|
+
expect(root.props.collapsable).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("renders focused as default when false", () => {
|
|
126
|
+
const wrapper = renderCell({ interactive: false }, "focused");
|
|
127
|
+
|
|
128
|
+
expect(wrapper.getByTestId("state-default")).toBeDefined();
|
|
129
|
+
expect(wrapper.queryByTestId("state-focused")).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("renders focused_selected as selected when false", () => {
|
|
133
|
+
const wrapper = renderCell({ interactive: false }, "focused_selected");
|
|
134
|
+
|
|
135
|
+
expect(wrapper.getByTestId("state-selected")).toBeDefined();
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
79
139
|
describe.skip("when using custom default components", () => {
|
|
80
140
|
// the collapsable prop is added here to easily check from the snapshot that the mock
|
|
81
141
|
// component is being used instead of the default one
|
|
@@ -10,7 +10,11 @@ import { useScreenState } from "@applicaster/zapp-react-native-utils/screenState
|
|
|
10
10
|
import { defaultComponents } from "./DefaultComponents";
|
|
11
11
|
import { defaultDataAdapter } from "./dataAdapter";
|
|
12
12
|
import { elementMapper, mapElementWithKey } from "./elementMapper";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getEntryState,
|
|
15
|
+
hasFocusableInsideBuilder,
|
|
16
|
+
withoutFocusedState,
|
|
17
|
+
} from "./utils";
|
|
14
18
|
|
|
15
19
|
type Props = {
|
|
16
20
|
item: ZappEntry;
|
|
@@ -19,6 +23,16 @@ type Props = {
|
|
|
19
23
|
|
|
20
24
|
const defaultContainerStyle = { flex: 1 };
|
|
21
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Root props of a non-interactive cell: the root view claims touches on the cell body,
|
|
28
|
+
* so the host list's touchable never fires (buttons, being deeper, still receive theirs).
|
|
29
|
+
* `collapsable: false` keeps Android / Fabric from flattening the view away.
|
|
30
|
+
*/
|
|
31
|
+
const nonInteractiveRootProps = {
|
|
32
|
+
onStartShouldSetResponder: () => true,
|
|
33
|
+
collapsable: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
22
36
|
/**
|
|
23
37
|
* Constructs a MasterCell with the provided options
|
|
24
38
|
* @param {Object} options
|
|
@@ -27,6 +41,8 @@ const defaultContainerStyle = { flex: 1 };
|
|
|
27
41
|
* @param {Function} options.dataAdapter optional dataAdapter to use instead of the default one.
|
|
28
42
|
* See dataAdapter.js for more info
|
|
29
43
|
* @param {containerStyles} options.containerStyle style to apply at the top level of the master cell
|
|
44
|
+
* @param {Object} options.cellOptions optional cell options. `interactive: false` makes the cell body
|
|
45
|
+
* ignore taps and render without focused / pressed states (inner buttons keep working). Defaults to `true`.
|
|
30
46
|
* @returns {Function} Functional React Component to render the masterCell with the provided options
|
|
31
47
|
*/
|
|
32
48
|
|
|
@@ -39,6 +55,7 @@ type MasterCellBuilderOptions = {
|
|
|
39
55
|
isRTL?: boolean;
|
|
40
56
|
skipButtons?: boolean;
|
|
41
57
|
component?: ZappComponent;
|
|
58
|
+
interactive?: boolean;
|
|
42
59
|
};
|
|
43
60
|
};
|
|
44
61
|
|
|
@@ -49,7 +66,7 @@ export function masterCellBuilder({
|
|
|
49
66
|
containerStyle = defaultContainerStyle,
|
|
50
67
|
cellOptions = {},
|
|
51
68
|
}: MasterCellBuilderOptions) {
|
|
52
|
-
const { skipButtons = false, component } = cellOptions;
|
|
69
|
+
const { skipButtons = false, component, interactive = true } = cellOptions;
|
|
53
70
|
|
|
54
71
|
const componentsMap = R.merge(defaultComponents, components);
|
|
55
72
|
const { View } = componentsMap;
|
|
@@ -88,7 +105,10 @@ export function masterCellBuilder({
|
|
|
88
105
|
() =>
|
|
89
106
|
elementsBuilder({
|
|
90
107
|
entry: item,
|
|
91
|
-
state: getEntryState(
|
|
108
|
+
state: getEntryState(
|
|
109
|
+
interactive ? state : withoutFocusedState(state),
|
|
110
|
+
entryIsSelected
|
|
111
|
+
),
|
|
92
112
|
allowDynamicColorsOutsideExtensions:
|
|
93
113
|
theme?.allow_dynamic_colors_outside_extensions,
|
|
94
114
|
}),
|
|
@@ -134,7 +154,11 @@ export function masterCellBuilder({
|
|
|
134
154
|
);
|
|
135
155
|
|
|
136
156
|
return (
|
|
137
|
-
<View
|
|
157
|
+
<View
|
|
158
|
+
style={containerStyle}
|
|
159
|
+
ref={wrapperRef}
|
|
160
|
+
{...(interactive ? undefined : nonInteractiveRootProps)}
|
|
161
|
+
>
|
|
138
162
|
{elements.map(remapElements)}
|
|
139
163
|
</View>
|
|
140
164
|
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { withoutFocusedState } from "..";
|
|
2
|
+
|
|
3
|
+
describe("withoutFocusedState", () => {
|
|
4
|
+
it("maps focused to default", () => {
|
|
5
|
+
expect(withoutFocusedState("focused")).toBe("default");
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("maps focused_selected to selected", () => {
|
|
9
|
+
expect(withoutFocusedState("focused_selected")).toBe("selected");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("keeps default and selected states", () => {
|
|
13
|
+
expect(withoutFocusedState("default")).toBe("default");
|
|
14
|
+
expect(withoutFocusedState("selected")).toBe("selected");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("keeps a missing state", () => {
|
|
18
|
+
expect(withoutFocusedState(undefined)).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -386,3 +386,21 @@ export function getEntryState(state, selected) {
|
|
|
386
386
|
|
|
387
387
|
return selected ? "selected" : "default";
|
|
388
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Drops focused (pressed) variants of a cell state, so a non-interactive cell
|
|
392
|
+
* always renders its idle look. `selected` is kept – it is not touch feedback.
|
|
393
|
+
*/
|
|
394
|
+
export function withoutFocusedState(
|
|
395
|
+
state?: CellState | null
|
|
396
|
+
): CellState | null | undefined {
|
|
397
|
+
if (state === "focused_selected") {
|
|
398
|
+
return "selected";
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (state === "focused") {
|
|
402
|
+
return "default";
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return state;
|
|
406
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.119",
|
|
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",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.119",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.119",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.119",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.119",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|