@applicaster/zapp-react-native-ui-components 16.0.0-rc.63 → 16.0.0-rc.65
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
2
|
import { Switch } from "react-native";
|
|
3
3
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
4
|
+
import { useCellClick } from "@applicaster/zapp-react-native-utils/reactHooks/cell-click";
|
|
4
5
|
|
|
5
6
|
type Props = {
|
|
6
7
|
item: ZappEntry | ZappFeed;
|
|
@@ -15,13 +16,7 @@ type Props = {
|
|
|
15
16
|
export const Toggle = (props: Props) => {
|
|
16
17
|
const { style, item, action, cellState } = props;
|
|
17
18
|
|
|
18
|
-
const {
|
|
19
|
-
trackColor,
|
|
20
|
-
trackColorActive,
|
|
21
|
-
thumbColor,
|
|
22
|
-
thumbColorActive,
|
|
23
|
-
iosBackgroundColor,
|
|
24
|
-
} = style;
|
|
19
|
+
const { trackColor, trackColorActive, thumbColor, thumbColorActive } = style;
|
|
25
20
|
|
|
26
21
|
const isSelected = React.useMemo(
|
|
27
22
|
() => cellState === "selected",
|
|
@@ -29,22 +24,43 @@ export const Toggle = (props: Props) => {
|
|
|
29
24
|
);
|
|
30
25
|
|
|
31
26
|
const actionContext = useActions(action?.identifier);
|
|
27
|
+
const onCellClick = useCellClick({ item: item as ZappEntry });
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
// A form toggle has no action plugin behind the switch: it reflects the cell
|
|
30
|
+
// selection state and writes through the entry's tap actions, the same path a
|
|
31
|
+
// tap on the row takes.
|
|
32
|
+
//
|
|
33
|
+
// The identifier alone cannot tell that case apart from a real action plugin.
|
|
34
|
+
// The cell manifest declares an initial_value for `toggle_action_identifier`,
|
|
35
|
+
// and `castOrDefault` substitutes that default whenever the configured value
|
|
36
|
+
// is empty - and an empty string counts as empty. Clearing the field in the
|
|
37
|
+
// app configuration therefore cannot produce an empty identifier here. What
|
|
38
|
+
// does distinguish the two is whether the identifier resolves to an installed
|
|
39
|
+
// action plugin, so that is what the route is decided on.
|
|
40
|
+
const hasActionPlugin = Boolean(
|
|
41
|
+
action?.identifier && actionContext?.invokeAction
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const [actionState, setActionState] = useState(() =>
|
|
45
|
+
hasActionPlugin ? actionContext?.initialEntryState?.(item) : undefined
|
|
35
46
|
);
|
|
36
47
|
|
|
37
48
|
const onPress = useCallback(() => {
|
|
38
|
-
|
|
49
|
+
if (!hasActionPlugin) {
|
|
50
|
+
onCellClick();
|
|
51
|
+
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
actionContext?.invokeAction?.(item, {
|
|
39
56
|
updateState: setActionState,
|
|
40
57
|
});
|
|
41
|
-
}, [actionState, actionContext?.state]);
|
|
58
|
+
}, [hasActionPlugin, onCellClick, actionState, actionContext?.state]);
|
|
42
59
|
|
|
43
60
|
return (
|
|
44
61
|
<Switch
|
|
45
62
|
trackColor={{ false: trackColor, true: trackColorActive }}
|
|
46
63
|
thumbColor={isSelected ? thumbColorActive : thumbColor}
|
|
47
|
-
ios_backgroundColor={iosBackgroundColor}
|
|
48
64
|
onValueChange={onPress}
|
|
49
65
|
value={isSelected}
|
|
50
66
|
/>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
4
|
+
|
|
5
|
+
import { Toggle } from "../Toggle";
|
|
6
|
+
|
|
7
|
+
const mockOnCellClick = jest.fn();
|
|
8
|
+
const mockInvokeAction = jest.fn();
|
|
9
|
+
const mockInitialEntryState = jest.fn().mockReturnValue({ state: 0 });
|
|
10
|
+
|
|
11
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/cell-click", () => ({
|
|
12
|
+
useCellClick: jest.fn(() => mockOnCellClick),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
|
|
16
|
+
useActions: jest.fn(() => ({
|
|
17
|
+
invokeAction: mockInvokeAction,
|
|
18
|
+
initialEntryState: mockInitialEntryState,
|
|
19
|
+
})),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
const style = {
|
|
23
|
+
trackColor: "rgba(0,0,0,1)",
|
|
24
|
+
trackColorActive: "rgba(4,207,153,1)",
|
|
25
|
+
thumbColor: "rgba(255,255,255,1)",
|
|
26
|
+
thumbColorActive: "rgba(255,255,255,1)",
|
|
27
|
+
iosBackgroundColor: "rgba(0,0,0,1)",
|
|
28
|
+
} as any;
|
|
29
|
+
|
|
30
|
+
const item = { id: "true", title: "I agree" } as any;
|
|
31
|
+
|
|
32
|
+
const renderToggle = (identifier: string, cellState) =>
|
|
33
|
+
render(
|
|
34
|
+
<Toggle
|
|
35
|
+
item={item}
|
|
36
|
+
action={{ identifier }}
|
|
37
|
+
style={style}
|
|
38
|
+
cellState={cellState}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
describe("Toggle with an empty action identifier", () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("is on when the cell is selected", () => {
|
|
48
|
+
const { getByRole } = renderToggle("", "selected");
|
|
49
|
+
|
|
50
|
+
expect(getByRole("switch").props.value).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("is off when the cell is not selected", () => {
|
|
54
|
+
const { getByRole } = renderToggle("", "default");
|
|
55
|
+
|
|
56
|
+
expect(getByRole("switch").props.value).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("routes a change through the cell click path", () => {
|
|
60
|
+
const { getByRole } = renderToggle("", "default");
|
|
61
|
+
|
|
62
|
+
fireEvent(getByRole("switch"), "valueChange", true);
|
|
63
|
+
|
|
64
|
+
expect(mockOnCellClick).toHaveBeenCalledTimes(1);
|
|
65
|
+
expect(mockInvokeAction).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("does not read the action plugin state", () => {
|
|
69
|
+
renderToggle("", "default");
|
|
70
|
+
|
|
71
|
+
expect(mockInitialEntryState).not.toHaveBeenCalled();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("Toggle with an action plugin identifier", () => {
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
jest.clearAllMocks();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("routes a change through the action plugin", () => {
|
|
81
|
+
const { getByRole } = renderToggle("firebase-push-topic-action", "default");
|
|
82
|
+
|
|
83
|
+
fireEvent(getByRole("switch"), "valueChange", true);
|
|
84
|
+
|
|
85
|
+
expect(mockInvokeAction).toHaveBeenCalledWith(
|
|
86
|
+
item,
|
|
87
|
+
expect.objectContaining({ updateState: expect.any(Function) })
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(mockOnCellClick).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("Toggle with an identifier whose action plugin is not installed", () => {
|
|
95
|
+
// The cell manifest declares initial_value "firebase-push-topic-action" for
|
|
96
|
+
// toggle_action_identifier, and castOrDefault replaces an empty configured
|
|
97
|
+
// value with that default. A form toggle therefore always arrives with a
|
|
98
|
+
// non-empty identifier it never asked for - the presence of a real action
|
|
99
|
+
// context, not the identifier string, is what decides the route.
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
jest.clearAllMocks();
|
|
102
|
+
|
|
103
|
+
(useActions as jest.Mock).mockReturnValue(undefined);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
(useActions as jest.Mock).mockReturnValue({
|
|
108
|
+
invokeAction: mockInvokeAction,
|
|
109
|
+
initialEntryState: mockInitialEntryState,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("routes a change through the cell click path", () => {
|
|
114
|
+
const { getByRole } = renderToggle("firebase-push-topic-action", "default");
|
|
115
|
+
|
|
116
|
+
fireEvent(getByRole("switch"), "valueChange", true);
|
|
117
|
+
|
|
118
|
+
expect(mockOnCellClick).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(mockInvokeAction).not.toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("still reflects the cell selection state", () => {
|
|
123
|
+
const { getByRole } = renderToggle(
|
|
124
|
+
"firebase-push-topic-action",
|
|
125
|
+
"selected"
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(getByRole("switch").props.value).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
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.65",
|
|
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.65",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.65",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.65",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.65",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|