@applicaster/zapp-react-native-ui-components 16.0.0-rc.64 → 16.0.0-rc.66

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.
@@ -16,10 +16,19 @@ type Props = {
16
16
  export const Toggle = (props: Props) => {
17
17
  const { style, item, action, cellState } = props;
18
18
 
19
- const { trackColor, trackColorActive, thumbColor, thumbColorActive } = style;
19
+ const {
20
+ trackColor,
21
+ trackColorActive,
22
+ thumbColor,
23
+ thumbColorActive,
24
+ iosBackgroundColor,
25
+ } = style;
20
26
 
27
+ // `focused_selected` is the selected state with a finger on the row, so the
28
+ // switch has to read as on for both. Leaving it out reads a pressed selected
29
+ // row as off, which flips the thumb for the duration of the press.
21
30
  const isSelected = React.useMemo(
22
- () => cellState === "selected",
31
+ () => cellState === "selected" || cellState === "focused_selected",
23
32
  [item?.id, cellState]
24
33
  );
25
34
 
@@ -30,13 +39,16 @@ export const Toggle = (props: Props) => {
30
39
  // selection state and writes through the entry's tap actions, the same path a
31
40
  // tap on the row takes.
32
41
  //
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.
42
+ // An empty identifier is the way to ask for that, which is why the cell
43
+ // manifest declares no initial_value for `toggle_action_identifier`:
44
+ // `castOrDefault` substitutes the default whenever the configured value is
45
+ // empty - and an empty string counts as empty - so any default there would be
46
+ // uncleanable and would force every toggle onto that plugin.
47
+ //
48
+ // The route is still decided on whether the identifier resolves to an
49
+ // installed action plugin rather than on the string being non-empty. App
50
+ // configurations saved while the old default was in the manifest still carry
51
+ // it, and a mistyped plugin id has to fall back the same way.
40
52
  const hasActionPlugin = Boolean(
41
53
  action?.identifier && actionContext?.invokeAction
42
54
  );
@@ -61,6 +73,7 @@ export const Toggle = (props: Props) => {
61
73
  <Switch
62
74
  trackColor={{ false: trackColor, true: trackColorActive }}
63
75
  thumbColor={isSelected ? thumbColorActive : thumbColor}
76
+ ios_backgroundColor={iosBackgroundColor}
64
77
  onValueChange={onPress}
65
78
  value={isSelected}
66
79
  />
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { fireEvent, render } from "@testing-library/react-native";
3
+ import { Switch } from "react-native";
3
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
4
5
 
5
6
  import { Toggle } from "../Toggle";
@@ -56,6 +57,21 @@ describe("Toggle with an empty action identifier", () => {
56
57
  expect(getByRole("switch").props.value).toBe(false);
57
58
  });
58
59
 
60
+ // A finger on a selected row resolves the cell to focused_selected. Reading
61
+ // that as unselected flipped the thumb for the duration of the press and let
62
+ // it jump back on release, which reads as a second tap.
63
+ it("stays on while a selected cell is pressed", () => {
64
+ const { getByRole } = renderToggle("", "focused_selected");
65
+
66
+ expect(getByRole("switch").props.value).toBe(true);
67
+ });
68
+
69
+ it("is off while an unselected cell is pressed", () => {
70
+ const { getByRole } = renderToggle("", "focused");
71
+
72
+ expect(getByRole("switch").props.value).toBe(false);
73
+ });
74
+
59
75
  it("routes a change through the cell click path", () => {
60
76
  const { getByRole } = renderToggle("", "default");
61
77
 
@@ -70,6 +86,23 @@ describe("Toggle with an empty action identifier", () => {
70
86
 
71
87
  expect(mockInitialEntryState).not.toHaveBeenCalled();
72
88
  });
89
+
90
+ // ios_backgroundColor paints behind the track, so trackColor does not cover
91
+ // it and it has to be forwarded on its own. It was dropped once while the
92
+ // destructure feeding it was being reformatted, and nothing caught that.
93
+ //
94
+ // Asserted on the Switch element rather than the rendered host component:
95
+ // what Toggle owns is the props it hands to Switch, and React Native rewrites
96
+ // them into platform-specific ones (tintColor, a style entry) underneath.
97
+ it("forwards every configured color to the switch", () => {
98
+ const { UNSAFE_getByType } = renderToggle("", "default");
99
+
100
+ expect(UNSAFE_getByType(Switch).props).toMatchObject({
101
+ trackColor: { false: style.trackColor, true: style.trackColorActive },
102
+ thumbColor: style.thumbColor,
103
+ ios_backgroundColor: style.iosBackgroundColor,
104
+ });
105
+ });
73
106
  });
74
107
 
75
108
  describe("Toggle with an action plugin identifier", () => {
@@ -92,11 +125,11 @@ describe("Toggle with an action plugin identifier", () => {
92
125
  });
93
126
 
94
127
  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.
128
+ // The cell manifest no longer defaults toggle_action_identifier, but app
129
+ // configurations saved while it did still carry "firebase-push-topic-action",
130
+ // and a mistyped id is always possible. A toggle can therefore still arrive
131
+ // with a non-empty identifier it never asked for - the presence of a real
132
+ // action context, not the identifier string, is what decides the route.
100
133
  beforeEach(() => {
101
134
  jest.clearAllMocks();
102
135
 
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.64",
3
+ "version": "16.0.0-rc.66",
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.64",
32
- "@applicaster/zapp-react-native-bridge": "16.0.0-rc.64",
33
- "@applicaster/zapp-react-native-redux": "16.0.0-rc.64",
34
- "@applicaster/zapp-react-native-utils": "16.0.0-rc.64",
31
+ "@applicaster/applicaster-types": "16.0.0-rc.66",
32
+ "@applicaster/zapp-react-native-bridge": "16.0.0-rc.66",
33
+ "@applicaster/zapp-react-native-redux": "16.0.0-rc.66",
34
+ "@applicaster/zapp-react-native-utils": "16.0.0-rc.66",
35
35
  "fast-json-stable-stringify": "^2.1.0",
36
36
  "promise": "^8.3.0",
37
37
  "react-native-sortables": "1.7.1",