Browse Source

Button color (#3713)

* Revert "merging buttons fix"

This reverts commit 74077bbc55.

* Revert "fix undefined buttons"

This reverts commit e3b4d15a06.

* Revert "fix e2e"

This reverts commit 05b3f5808f.

* Revert "Handle merge buttons style"

This reverts commit 9c705402eb.

* Add background component with MATCH_PARENT height

* temporary solution for merging button color

This commit introduces two temporary options to control button colors
* rightButtonColor
* leftButtonColor

These options can be used to color buttons. Colors defined in buttons take
precedence over these two options.

* Get buttonColor options from resolved options

* leftButtonColor & rightButtonColor support - iOS

* Disabled color

* disabled buttons color support

* Rebase fixes

* empty commit
Yogev Ben David 6 years ago
parent
commit
96c070495d
No account linked to committer's email address

+ 29
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java View File

@@ -43,6 +43,9 @@ public class TopBarOptions {
43 43
         options.elevation = FractionParser.parse(json, "elevation");
44 44
         options.buttons = TopBarButtons.parse(typefaceLoader, json);
45 45
 
46
+        options.rightButtonColor = ColorParser.parse(json, "rightButtonColor");
47
+        options.leftButtonColor = ColorParser.parse(json, "leftButtonColor");
48
+
46 49
         options.validate();
47 50
         return options;
48 51
     }
@@ -61,6 +64,18 @@ public class TopBarOptions {
61 64
     public Fraction borderHeight = new NullFraction();
62 65
     public Color borderColor = new NullColor();
63 66
 
67
+    // Deprecated
68
+    public Color rightButtonColor = new NullColor();
69
+    public Color leftButtonColor = new NullColor();
70
+    public Color rightButtonDisabledColor = new NullColor();
71
+    public Color leftButtonDisabledColor = new NullColor();
72
+
73
+    public TopBarOptions copy() {
74
+        TopBarOptions result = new TopBarOptions();
75
+        result.mergeWith(this);
76
+        return result;
77
+    }
78
+
64 79
     void mergeWith(final TopBarOptions other) {
65 80
         title.mergeWith(other.title);
66 81
         subtitle.mergeWith(other.subtitle);
@@ -75,10 +90,16 @@ public class TopBarOptions {
75 90
         if (other.borderHeight.hasValue()) borderHeight = other.borderHeight;
76 91
         if (other.borderColor.hasValue()) borderColor = other.borderColor;
77 92
         if (other.elevation.hasValue()) elevation = other.elevation;
93
+
94
+        if (other.rightButtonColor.hasValue()) rightButtonColor = other.rightButtonColor;
95
+        if (other.leftButtonColor.hasValue()) leftButtonColor = other.leftButtonColor;
96
+        if (other.rightButtonDisabledColor.hasValue()) rightButtonDisabledColor = other.rightButtonDisabledColor;
97
+        if (other.leftButtonDisabledColor.hasValue()) leftButtonDisabledColor = other.leftButtonDisabledColor;
98
+
78 99
         validate();
79 100
     }
80 101
 
81
-    void mergeWithDefault(TopBarOptions defaultOptions) {
102
+    public TopBarOptions mergeWithDefault(TopBarOptions defaultOptions) {
82 103
         title.mergeWithDefault(defaultOptions.title);
83 104
         subtitle.mergeWithDefault(defaultOptions.subtitle);
84 105
         background.mergeWithDefault(defaultOptions.background);
@@ -92,7 +113,14 @@ public class TopBarOptions {
92 113
         if (!borderHeight.hasValue()) borderHeight = defaultOptions.borderHeight;
93 114
         if (!borderColor.hasValue()) borderColor = defaultOptions.borderColor;
94 115
         if (!elevation.hasValue()) elevation = defaultOptions.elevation;
116
+
117
+        if (!rightButtonColor.hasValue()) rightButtonColor = defaultOptions.rightButtonColor;
118
+        if (!leftButtonColor.hasValue()) leftButtonColor = defaultOptions.leftButtonColor;
119
+        if (!rightButtonDisabledColor.hasValue()) rightButtonDisabledColor = defaultOptions.rightButtonDisabledColor;
120
+        if (!leftButtonDisabledColor.hasValue()) leftButtonDisabledColor = defaultOptions.leftButtonDisabledColor;
121
+
95 122
         validate();
123
+        return this;
96 124
     }
97 125
 
98 126
     public void validate() {

+ 8
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java View File

@@ -31,7 +31,7 @@ public class Button {
31 31
     public Text testId = new NullText();
32 32
     public Component component = new Component();
33 33
 
34
-    protected static Button parseJson(JSONObject json, TypefaceLoader typefaceManager) {
34
+    private static Button parseJson(JSONObject json, TypefaceLoader typefaceManager) {
35 35
         Button button = new Button();
36 36
         button.id = json.optString("id");
37 37
         button.text = TextParser.parse(json, "text");
@@ -78,6 +78,12 @@ public class Button {
78 78
         return buttons;
79 79
     }
80 80
 
81
+    public Button copy() {
82
+        Button button = new Button();
83
+        button.mergeWith(this);
84
+        return button;
85
+    }
86
+
81 87
     public boolean hasComponent() {
82 88
         return component.hasValue();
83 89
     }
@@ -118,6 +124,7 @@ public class Button {
118 124
         if (other.component.hasValue()) component = other.component;
119 125
         if (other.showAsAction.hasValue()) showAsAction = other.showAsAction;
120 126
         if (other.icon.hasValue()) icon = other.icon;
127
+        if (other.id != null) id = other.id;
121 128
     }
122 129
 
123 130
     public void mergeWithDefault(Button defaultOptions) {

+ 47
- 16
lib/android/app/src/main/java/com/reactnativenavigation/presentation/StackOptionsPresenter.java View File

@@ -3,6 +3,7 @@ package com.reactnativenavigation.presentation;
3 3
 import android.app.Activity;
4 4
 import android.content.Context;
5 5
 import android.graphics.Color;
6
+import android.support.annotation.Nullable;
6 7
 import android.view.View;
7 8
 import android.view.ViewGroup.LayoutParams;
8 9
 
@@ -13,15 +14,20 @@ import com.reactnativenavigation.parse.TopBarButtons;
13 14
 import com.reactnativenavigation.parse.TopBarOptions;
14 15
 import com.reactnativenavigation.parse.TopTabOptions;
15 16
 import com.reactnativenavigation.parse.TopTabsOptions;
17
+import com.reactnativenavigation.parse.params.Button;
16 18
 import com.reactnativenavigation.utils.UiUtils;
17 19
 import com.reactnativenavigation.viewcontrollers.IReactView;
18 20
 import com.reactnativenavigation.views.Component;
19 21
 import com.reactnativenavigation.views.topbar.TopBar;
20 22
 
23
+import java.util.ArrayList;
24
+import java.util.List;
25
+
21 26
 public class StackOptionsPresenter {
22 27
     private static final int DEFAULT_TITLE_COLOR = Color.BLACK;
23 28
     private static final int DEFAULT_SUBTITLE_COLOR = Color.GRAY;
24 29
     private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
30
+    private static final double DEFAULT_ELEVATION = 4d;
25 31
     private final double defaultTitleFontSize;
26 32
     private final double defaultSubtitleFontSize;
27 33
 
@@ -59,12 +65,12 @@ public class StackOptionsPresenter {
59 65
     }
60 66
 
61 67
     public void applyChildOptions(Options options, Component child) {
62
-        Options withDefaultOptions = options.copy().withDefaultOptions(defaultOptions);
63
-        applyOrientation(withDefaultOptions.layout.orientation);
64
-        applyButtons(withDefaultOptions.topBar.buttons);
65
-        applyTopBarOptions(withDefaultOptions.topBar, withDefaultOptions.animations, child, options);
66
-        applyTopTabsOptions(withDefaultOptions.topTabs);
67
-        applyTopTabOptions(withDefaultOptions.topTabOptions);
68
+        Options withDefault = options.copy().withDefaultOptions(defaultOptions);
69
+        applyOrientation(withDefault.layout.orientation);
70
+        applyButtons(withDefault.topBar, withDefault.topBar.rightButtonColor, withDefault.topBar.leftButtonColor, withDefault.topBar.rightButtonDisabledColor, withDefault.topBar.leftButtonDisabledColor);
71
+        applyTopBarOptions(withDefault.topBar, withDefault.animations, child, options);
72
+        applyTopTabsOptions(withDefault.topTabs);
73
+        applyTopTabOptions(withDefault.topTabOptions);
68 74
     }
69 75
 
70 76
     public void applyOrientation(OrientationOptions options) {
@@ -74,7 +80,7 @@ public class StackOptionsPresenter {
74 80
 
75 81
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component, Options componentOptions) {
76 82
         topBar.setHeight(options.height.get(LayoutParams.WRAP_CONTENT));
77
-        topBar.setElevation(options.elevation.get(4d));
83
+        topBar.setElevation(options.elevation.get(DEFAULT_ELEVATION));
78 84
 
79 85
         topBar.setTitleHeight(options.title.height.get(LayoutParams.WRAP_CONTENT));
80 86
         topBar.setTitle(options.title.text.get(""));
@@ -128,10 +134,12 @@ public class StackOptionsPresenter {
128 134
         }
129 135
     }
130 136
 
131
-    private void applyButtons(TopBarButtons buttons) {
132
-        topBar.setLeftButtons(buttons.left);
133
-        topBar.setRightButtons(buttons.right);
134
-        if (buttons.back.visible.isTrue() && !buttons.hasLeftButtons()) topBar.setBackButton(buttons.back);
137
+    private void applyButtons(TopBarOptions options, com.reactnativenavigation.parse.params.Color rightButtonColor, com.reactnativenavigation.parse.params.Color leftButtonColor, com.reactnativenavigation.parse.params.Color rightButtonDisabledColor, com.reactnativenavigation.parse.params.Color leftButtonDisabledColor) {
138
+        List<Button> rightButtons = mergeButtonsWithColor(options.buttons.right, rightButtonColor, rightButtonDisabledColor);
139
+        List<Button> leftButtons = mergeButtonsWithColor(options.buttons.left, leftButtonColor, leftButtonDisabledColor);
140
+        topBar.setRightButtons(rightButtons);
141
+        topBar.setLeftButtons(leftButtons);
142
+        if (options.buttons.back.visible.isTrue() && !options.buttons.hasLeftButtons()) topBar.setBackButton(options.buttons.back);
135 143
     }
136 144
 
137 145
     private void applyTopTabsOptions(TopTabsOptions options) {
@@ -155,9 +163,15 @@ public class StackOptionsPresenter {
155 163
         }
156 164
     }
157 165
 
158
-    public void mergeChildOptions(Options options, Component child) {
166
+    public void mergeChildOptions(Options options, Options childOptions, Component child) {
167
+        TopBarOptions topBar = options.copy().mergeWith(childOptions).withDefaultOptions(defaultOptions).topBar;
159 168
         mergeOrientation(options.layout.orientation);
160
-        mergeButtons(options.topBar.buttons);
169
+        mergeButtons(options.topBar.buttons,
170
+                topBar.rightButtonColor,
171
+                topBar.leftButtonColor,
172
+                topBar.rightButtonDisabledColor,
173
+                topBar.leftButtonDisabledColor
174
+        );
161 175
         mergeTopBarOptions(options.topBar, options.animations, child);
162 176
         mergeTopTabsOptions(options.topTabs);
163 177
         mergeTopTabOptions(options.topTabOptions);
@@ -167,12 +181,29 @@ public class StackOptionsPresenter {
167 181
         if (orientationOptions.hasValue()) applyOrientation(orientationOptions);
168 182
     }
169 183
 
170
-    private void mergeButtons(TopBarButtons buttons) {
171
-        if (buttons.left != null) topBar.setLeftButtons(buttons.left);
172
-        if (buttons.right != null) topBar.setRightButtons(buttons.right);
184
+    private void mergeButtons(TopBarButtons buttons, com.reactnativenavigation.parse.params.Color rightButtonColor, com.reactnativenavigation.parse.params.Color leftButtonColor, com.reactnativenavigation.parse.params.Color rightButtonDisabledColor, com.reactnativenavigation.parse.params.Color leftButtonDisabledColor) {
185
+        List<Button> rightButtons = mergeButtonsWithColor(buttons.right, rightButtonColor, rightButtonDisabledColor);
186
+        List<Button> leftButtons = mergeButtonsWithColor(buttons.left, leftButtonColor, leftButtonDisabledColor);
187
+        if (buttons.right != null) topBar.setRightButtons(rightButtons);
188
+        if (buttons.left != null) topBar.setLeftButtons(leftButtons);
173 189
         if (buttons.back.hasValue()) topBar.setBackButton(buttons.back);
174 190
     }
175 191
 
192
+    @Nullable
193
+    private List<Button> mergeButtonsWithColor(List<Button> buttons, com.reactnativenavigation.parse.params.Color buttonColor, com.reactnativenavigation.parse.params.Color disabledColor) {
194
+        List<Button> result = null;
195
+        if (buttons != null) {
196
+            result = new ArrayList<>();
197
+            for (Button button : buttons) {
198
+                Button copy = button.copy();
199
+                if (!button.color.hasValue()) copy.color = buttonColor;
200
+                if (!button.disabledColor.hasValue()) copy.disabledColor = disabledColor;
201
+                result.add(copy);
202
+            }
203
+        }
204
+        return result;
205
+    }
206
+
176 207
     private void mergeTopBarOptions(TopBarOptions options, AnimationsOptions animationsOptions, Component component) {
177 208
         if (options.height.hasValue()) topBar.setHeight(options.height.get());
178 209
         if (options.elevation.hasValue()) topBar.setElevation(options.elevation.get());

+ 7
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java View File

@@ -39,7 +39,13 @@ public class TopBarButtonController extends ViewController<TitleBarReactButtonVi
39 39
     private TopBarButtonController.OnClickListener onPressListener;
40 40
     private Drawable icon;
41 41
 
42
-    public TopBarButtonController(Activity activity, NavigationIconResolver navigationIconResolver, ImageLoader imageLoader, ButtonOptionsPresenter optionsPresenter, Button button, ReactViewCreator viewCreator, OnClickListener onClickListener) {
42
+    public TopBarButtonController(Activity activity,
43
+                                  NavigationIconResolver navigationIconResolver,
44
+                                  ImageLoader imageLoader,
45
+                                  ButtonOptionsPresenter optionsPresenter,
46
+                                  Button button,
47
+                                  ReactViewCreator viewCreator,
48
+                                  OnClickListener onClickListener) {
43 49
         super(activity, button.id, new Options());
44 50
         this.navigationIconResolver = navigationIconResolver;
45 51
         this.imageLoader = imageLoader;

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/stack/StackController.java View File

@@ -95,7 +95,7 @@ public class StackController extends ParentController<StackLayout> {
95 95
     @Override
96 96
     public void mergeChildOptions(Options options, Component child) {
97 97
         super.mergeChildOptions(options, child);
98
-        presenter.mergeChildOptions(options, child);
98
+        presenter.mergeChildOptions(options, resolveCurrentOptions(), child);
99 99
         if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
100 100
             fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
101 101
         }

+ 2
- 6
lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java View File

@@ -34,7 +34,6 @@ import com.reactnativenavigation.parse.params.Number;
34 34
 import com.reactnativenavigation.utils.CompatUtils;
35 35
 import com.reactnativenavigation.utils.ImageLoader;
36 36
 import com.reactnativenavigation.utils.UiUtils;
37
-import com.reactnativenavigation.utils.ViewUtils;
38 37
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
39 38
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
40 39
 import com.reactnativenavigation.viewcontrollers.topbar.TopBarBackgroundViewController;
@@ -55,8 +54,6 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
55 54
     private TopBarAnimator animator;
56 55
     private TopTabs topTabs;
57 56
     private FrameLayout root;
58
-    private LinearLayout content;
59
-    private StackLayout parentView;
60 57
     private TopBarBackgroundViewController topBarBackgroundViewController;
61 58
     private View border;
62 59
     private ImageLoader imageLoader;
@@ -67,7 +64,6 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
67 64
         this.imageLoader = imageLoader;
68 65
         collapsingBehavior = new TopBarCollapseBehavior(this);
69 66
         this.topBarBackgroundViewController = topBarBackgroundViewController;
70
-        this.parentView = parentView;
71 67
         topTabs = new TopTabs(getContext());
72 68
         animator = new TopBarAnimator(this, parentView.getStackId());
73 69
         createLayout(buttonCreator, titleBarReactViewCreator, onClickListener);
@@ -78,7 +74,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
78 74
         titleBar = createTitleBar(getContext(), buttonCreator, titleBarReactViewCreator, onClickListener, imageLoader);
79 75
         topTabs = createTopTabs();
80 76
         border = createBorder();
81
-        content = createContentLayout();
77
+        LinearLayout content = createContentLayout();
82 78
 
83 79
         root = new FrameLayout(getContext());
84 80
         root.setId(CompatUtils.generateViewId());
@@ -187,7 +183,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
187 183
     public void setBackgroundComponent(Component component) {
188 184
         if (component.hasValue()) {
189 185
             topBarBackgroundViewController.setComponent(component);
190
-            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, ViewUtils.getPreferredHeight(this));
186
+            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
191 187
             root.addView(topBarBackgroundViewController.getView(), 0, lp);
192 188
         }
193 189
     }

+ 113
- 13
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackOptionsPresenterTest.java View File

@@ -12,6 +12,7 @@ import com.reactnativenavigation.parse.OrientationOptions;
12 12
 import com.reactnativenavigation.parse.SubtitleOptions;
13 13
 import com.reactnativenavigation.parse.TitleOptions;
14 14
 import com.reactnativenavigation.parse.params.Bool;
15
+import com.reactnativenavigation.parse.params.Button;
15 16
 import com.reactnativenavigation.parse.params.Color;
16 17
 import com.reactnativenavigation.parse.params.Fraction;
17 18
 import com.reactnativenavigation.parse.params.Number;
@@ -22,10 +23,13 @@ import com.reactnativenavigation.views.topbar.TopBar;
22 23
 
23 24
 import org.json.JSONObject;
24 25
 import org.junit.Test;
26
+import org.mockito.ArgumentCaptor;
25 27
 import org.mockito.Mockito;
26 28
 
27 29
 import java.util.ArrayList;
30
+import java.util.List;
28 31
 
32
+import static org.assertj.core.api.Java6Assertions.assertThat;
29 33
 import static org.mockito.ArgumentMatchers.any;
30 34
 import static org.mockito.ArgumentMatchers.anyBoolean;
31 35
 import static org.mockito.ArgumentMatchers.anyDouble;
@@ -38,6 +42,7 @@ import static org.mockito.Mockito.when;
38 42
 
39 43
 public class StackOptionsPresenterTest extends BaseTest {
40 44
 
45
+    private static final Options EMPTY_OPTIONS = new Options();
41 46
     private StackOptionsPresenter uut;
42 47
     private TestComponentLayout child;
43 48
     private Activity activity;
@@ -55,35 +60,35 @@ public class StackOptionsPresenterTest extends BaseTest {
55 60
     @Test
56 61
     public void mergeOrientation() throws Exception {
57 62
         Options options = new Options();
58
-        uut.mergeChildOptions(options, child);
63
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
59 64
         verify(uut, times(0)).applyOrientation(any());
60 65
 
61 66
         JSONObject orientation = new JSONObject().put("orientation", "landscape");
62 67
         options.layout.orientation = OrientationOptions.parse(orientation);
63
-        uut.mergeChildOptions(options, child);
68
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
64 69
         verify(uut, times(1)).applyOrientation(options.layout.orientation);
65 70
     }
66 71
 
67 72
     @Test
68 73
     public void mergeButtons() {
69 74
         Options options = new Options();
70
-        uut.mergeChildOptions(options, child);
75
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
71 76
         verify(topBar, times(0)).setRightButtons(any());
72 77
         verify(topBar, times(0)).setLeftButtons(any());
73 78
 
74 79
         options.topBar.buttons.right = new ArrayList<>();
75
-        uut.mergeChildOptions(options, child);
80
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
76 81
         verify(topBar, times(1)).setRightButtons(any());
77 82
 
78 83
         options.topBar.buttons.left = new ArrayList<>();
79
-        uut.mergeChildOptions(options, child);
84
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
80 85
         verify(topBar, times(1)).setLeftButtons(any());
81 86
     }
82 87
 
83 88
     @Test
84 89
     public void mergeTopBarOptions() {
85 90
         Options options = new Options();
86
-        uut.mergeChildOptions(options, child);
91
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
87 92
         assertTopBarOptions(options, 0);
88 93
 
89 94
         TitleOptions title = new TitleOptions();
@@ -105,19 +110,20 @@ public class StackOptionsPresenterTest extends BaseTest {
105 110
         options.topBar.drawBehind = new Bool(false);
106 111
         options.topBar.hideOnScroll = new Bool(false);
107 112
         options.topBar.validate();
108
-        uut.mergeChildOptions(options, child);
113
+
114
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
109 115
 
110 116
         assertTopBarOptions(options, 1);
111 117
 
112 118
         options.topBar.drawBehind = new Bool(true);
113
-        uut.mergeChildOptions(options, child);
119
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
114 120
         verify(child, times(1)).drawBehindTopBar();
115 121
     }
116 122
 
117 123
     @Test
118 124
     public void mergeTopTabsOptions() {
119 125
         Options options = new Options();
120
-        uut.mergeChildOptions(options, child);
126
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
121 127
         verify(topBar, times(0)).applyTopTabsColors(any(), any());
122 128
         verify(topBar, times(0)).applyTopTabsFontSize(any());
123 129
         verify(topBar, times(0)).setTopTabsVisible(anyBoolean());
@@ -126,7 +132,7 @@ public class StackOptionsPresenterTest extends BaseTest {
126 132
         options.topTabs.unselectedTabColor = new Color(1);
127 133
         options.topTabs.fontSize = new Number(1);
128 134
         options.topTabs.visible = new Bool(true);
129
-        uut.mergeChildOptions(options, child);
135
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
130 136
         verify(topBar, times(1)).applyTopTabsColors(options.topTabs.selectedTabColor, options.topTabs.unselectedTabColor);
131 137
         verify(topBar, times(1)).applyTopTabsFontSize(options.topTabs.fontSize);
132 138
         verify(topBar, times(1)).setTopTabsVisible(anyBoolean());
@@ -135,13 +141,13 @@ public class StackOptionsPresenterTest extends BaseTest {
135 141
     @Test
136 142
     public void mergeTopTabOptions() {
137 143
         Options options = new Options();
138
-        uut.mergeChildOptions(options, child);
144
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
139 145
 
140 146
         verify(topBar, times(0)).setTopTabFontFamily(anyInt(), any());
141 147
 
142 148
         options.topTabOptions.tabIndex = 1;
143 149
         options.topTabOptions.fontFamily = Typeface.DEFAULT_BOLD;
144
-        uut.mergeChildOptions(options, child);
150
+        uut.mergeChildOptions(options, EMPTY_OPTIONS, child);
145 151
 
146 152
         verify(topBar, times(1)).setTopTabFontFamily(1, Typeface.DEFAULT_BOLD);
147 153
     }
@@ -165,11 +171,105 @@ public class StackOptionsPresenterTest extends BaseTest {
165 171
 
166 172
         Options childOptions = new Options();
167 173
         childOptions.topBar.title.text = new Text("someText");
168
-        uut.mergeChildOptions(childOptions, child);
174
+        uut.mergeChildOptions(childOptions, EMPTY_OPTIONS, child);
169 175
 
170 176
         verify(topBar, times(0)).setBackgroundColor(anyInt());
171 177
     }
172 178
 
179
+    public void applyButtons_buttonColorIsMergedToButtons() {
180
+        Options options = new Options();
181
+        Button rightButton1 = new Button();
182
+        Button rightButton2 = new Button();
183
+        Button leftButton = new Button();
184
+
185
+        options.topBar.rightButtonColor = new Color(10);
186
+        options.topBar.leftButtonColor = new Color(100);
187
+
188
+        options.topBar.buttons.right = new ArrayList<>();
189
+        options.topBar.buttons.right.add(rightButton1);
190
+        options.topBar.buttons.right.add(rightButton2);
191
+
192
+        options.topBar.buttons.left = new ArrayList<>();
193
+        options.topBar.buttons.left.add(leftButton);
194
+
195
+        uut.applyChildOptions(options, child);
196
+        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
197
+        verify(topBar).setRightButtons(rightCaptor.capture());
198
+        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(options.topBar.rightButtonColor.get());
199
+        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(options.topBar.rightButtonColor.get());
200
+        assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
201
+        assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
202
+
203
+        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
204
+        verify(topBar).setLeftButtons(leftCaptor.capture());
205
+        assertThat(leftCaptor.getValue().get(0).color).isEqualTo(options.topBar.leftButtonColor);
206
+        assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
207
+    }
208
+
209
+    @Test
210
+    public void mergeChildOptions_buttonColorIsResolvedFromAppliedOptions() {
211
+        Options appliedOptions = new Options();
212
+        appliedOptions.topBar.rightButtonColor = new Color(10);
213
+        appliedOptions.topBar.leftButtonColor = new Color(100);
214
+
215
+        Options options2 = new Options();
216
+        Button rightButton1 = new Button();
217
+        Button rightButton2 = new Button();
218
+        Button leftButton = new Button();
219
+
220
+        options2.topBar.buttons.right = new ArrayList<>();
221
+        options2.topBar.buttons.right.add(rightButton1);
222
+        options2.topBar.buttons.right.add(rightButton2);
223
+
224
+        options2.topBar.buttons.left = new ArrayList<>();
225
+        options2.topBar.buttons.left.add(leftButton);
226
+
227
+        uut.mergeChildOptions(options2, appliedOptions, child);
228
+        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
229
+        verify(topBar, times(1)).setRightButtons(rightCaptor.capture());
230
+        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
231
+        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(appliedOptions.topBar.rightButtonColor.get());
232
+        assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
233
+        assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
234
+
235
+        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
236
+        verify(topBar, times(1)).setLeftButtons(leftCaptor.capture());
237
+        assertThat(leftCaptor.getValue().get(0).color.get()).isEqualTo(appliedOptions.topBar.leftButtonColor.get());
238
+        assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
239
+    }
240
+
241
+    @Test
242
+    public void mergeChildOptions_buttonColorIsResolvedFromMergedOptions() {
243
+        Options resolvedOptions = new Options();
244
+        resolvedOptions.topBar.rightButtonColor = new Color(10);
245
+        resolvedOptions.topBar.leftButtonColor = new Color(100);
246
+
247
+        Options options2 = new Options();
248
+        Button rightButton1 = new Button();
249
+        Button rightButton2 = new Button();
250
+        Button leftButton = new Button();
251
+
252
+        options2.topBar.buttons.right = new ArrayList<>();
253
+        options2.topBar.buttons.right.add(rightButton1);
254
+        options2.topBar.buttons.right.add(rightButton2);
255
+
256
+        options2.topBar.buttons.left = new ArrayList<>();
257
+        options2.topBar.buttons.left.add(leftButton);
258
+
259
+        uut.mergeChildOptions(options2, resolvedOptions, child);
260
+        ArgumentCaptor<List<Button>> rightCaptor = ArgumentCaptor.forClass(List.class);
261
+        verify(topBar).setRightButtons(rightCaptor.capture());
262
+        assertThat(rightCaptor.getValue().get(0).color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
263
+        assertThat(rightCaptor.getValue().get(1).color.get()).isEqualTo(resolvedOptions.topBar.rightButtonColor.get());
264
+        assertThat(rightCaptor.getValue().get(0)).isNotEqualTo(rightButton1);
265
+        assertThat(rightCaptor.getValue().get(1)).isNotEqualTo(rightButton2);
266
+
267
+        ArgumentCaptor<List<Button>> leftCaptor = ArgumentCaptor.forClass(List.class);
268
+        verify(topBar).setLeftButtons(leftCaptor.capture());
269
+        assertThat(leftCaptor.getValue().get(0).color.get()).isEqualTo(resolvedOptions.topBar.leftButtonColor.get());
270
+        assertThat(leftCaptor.getValue().get(0)).isNotEqualTo(leftButton);
271
+    }
272
+
173 273
     private void assertTopBarOptions(Options options, int t) {
174 274
         if (options.topBar.title.component.hasValue()) {
175 275
             verify(topBar, times(0)).setTitle(any());

+ 1
- 1
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java View File

@@ -57,7 +57,7 @@ public class TopBarBackgroundComponentTest extends BaseTest {
57 57
         TopBarBackgroundView background = (TopBarBackgroundView) ViewUtils.findChildrenByClassRecursive(uut, TopBarBackgroundView.class).get(0);
58 58
         assertThat(background).isNotNull();
59 59
         assertThat(background.getLayoutParams().width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
60
-        assertThat(background.getLayoutParams().height).isEqualTo(100);
60
+        assertThat(background.getLayoutParams().height).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
61 61
     }
62 62
 
63 63
     @Test

+ 6
- 6
lib/ios/RNNNavigationButtons.m View File

@@ -107,18 +107,18 @@
107 107
 	NSMutableDictionary* disabledTextAttributes = [[NSMutableDictionary alloc] init];
108 108
 	
109 109
 	UIColor* color = [self color:dictionary[@"color"] defaultColor:defaultStyle.color];
110
+	UIColor* disabledColor = [self color:dictionary[@"disabledColor"] defaultColor:defaultStyle.disabledColor];
111
+	if (!enabledBool && disabledColor) {
112
+		color = disabledColor;
113
+		[disabledTextAttributes setObject:disabledColor forKey:NSForegroundColorAttributeName];
114
+	}
115
+	
110 116
 	if (color) {
111 117
 		[textAttributes setObject:color forKey:NSForegroundColorAttributeName];
112 118
 		[barButtonItem setImage:iconImage];
113 119
 		[barButtonItem setTintColor:color];
114 120
 	}
115 121
 	
116
-	UIColor* disabledColor = [self color:dictionary[@"disabledColor"] defaultColor:defaultStyle.disabledColor];;
117
-	if (disabledColor) {
118
-		UIColor *color = disabledColor;
119
-		[disabledTextAttributes setObject:color forKey:NSForegroundColorAttributeName];
120
-	}
121
-	
122 122
 	NSNumber* fontSize = [self fontSize:dictionary[@"fontSize"] defaultFontSize:defaultStyle.fontSize];
123 123
 	NSString* fontFamily = [self fontFamily:dictionary[@"fontFamily"] defaultFontFamily:defaultStyle.fontFamily];
124 124
 	UIFont *font = nil;

+ 4
- 1
lib/ios/RNNTopBarOptions.h View File

@@ -13,7 +13,10 @@
13 13
 @property (nonatomic, strong) NSArray* rightButtons;
14 14
 @property (nonatomic, strong) NSNumber* visible;
15 15
 @property (nonatomic, strong) NSNumber* hideOnScroll;
16
-@property (nonatomic, strong) NSNumber* buttonColor;
16
+@property (nonatomic, strong) NSNumber* leftButtonColor;
17
+@property (nonatomic, strong) NSNumber* rightButtonColor;
18
+@property (nonatomic, strong) NSNumber* leftButtonDisabledColor;
19
+@property (nonatomic, strong) NSNumber* rightButtonDisabledColor;
17 20
 @property (nonatomic, strong) NSString* barStyle;
18 21
 @property (nonatomic, strong) NSNumber* translucent;
19 22
 @property (nonatomic, strong) NSNumber* transparent;

+ 16
- 7
lib/ios/RNNTopBarOptions.m View File

@@ -149,13 +149,6 @@ extern const NSInteger BLUR_TOPBAR_TAG;
149 149
 		viewController.navigationController.navigationBar.accessibilityIdentifier = self.testID;
150 150
 	}
151 151
 	
152
-	if (self.buttonColor) {
153
-		UIColor* buttonColor = [RCTConvert UIColor:self.buttonColor];
154
-		viewController.navigationController.navigationBar.tintColor = buttonColor;
155
-	} else {
156
-		viewController.navigationController.navigationBar.tintColor = nil;
157
-	}
158
-	
159 152
 	if (self.rightButtons || self.leftButtons) {
160 153
 		_navigationButtons = [[RNNNavigationButtons alloc] initWithViewController:(RNNRootViewController*)viewController];
161 154
 		[_navigationButtons applyLeftButtons:self.leftButtons rightButtons:self.rightButtons defaultLeftButtonStyle:self.leftButtonStyle defaultRightButtonStyle:self.rightButtonStyle];
@@ -163,6 +156,14 @@ extern const NSInteger BLUR_TOPBAR_TAG;
163 156
 }
164 157
 
165 158
 - (void)setLeftButtons:(id)leftButtons {
159
+	if (self.leftButtonColor) {
160
+		_leftButtonStyle.color = self.leftButtonColor;
161
+	}
162
+	
163
+	if (self.leftButtonDisabledColor) {
164
+		_leftButtonStyle.disabledColor = self.leftButtonDisabledColor;
165
+	}
166
+	
166 167
 	if ([leftButtons isKindOfClass:[NSArray class]]) {
167 168
 		_leftButtons = leftButtons;
168 169
 	} else if ([leftButtons isKindOfClass:[NSDictionary class]]) {
@@ -175,6 +176,14 @@ extern const NSInteger BLUR_TOPBAR_TAG;
175 176
 }
176 177
 
177 178
 - (void)setRightButtons:(id)rightButtons {
179
+	if (self.rightButtonColor) {
180
+		_rightButtonStyle.color = self.rightButtonColor;
181
+	}
182
+	
183
+	if (self.rightButtonDisabledColor) {
184
+		_rightButtonStyle.disabledColor = self.rightButtonDisabledColor;
185
+	}
186
+	
178 187
 	if ([rightButtons isKindOfClass:[NSArray class]]) {
179 188
 		_rightButtons = rightButtons;
180 189
 	} else if ([rightButtons isKindOfClass:[NSDictionary class]]) {

+ 0
- 130
lib/src/commands/LayoutTreeCrawler.test.ts View File

@@ -104,136 +104,6 @@ describe('LayoutTreeCrawler', () => {
104 104
     });
105 105
   });
106 106
 
107
-  it('Components: extract button style from passedOptions buttons array and merge it to all buttons', () => {
108
-    const MyComponent = class {
109
-      static get options() {
110
-        return {
111
-          topBar: {
112
-            leftButtons: [{
113
-              id: 'id'
114
-            }, {
115
-              id: 'id2'
116
-            }],
117
-            rightButtons: [{
118
-              id: 'id3'
119
-            }]
120
-          }
121
-        };
122
-      }
123
-    };
124
-
125
-    const passedOptions = {
126
-      topBar: {
127
-        leftButtons: {
128
-          font: 'Helvetica'
129
-        },
130
-        rightButtons: []
131
-      }
132
-    };
133
-
134
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions } };
135
-    store.setOriginalComponentClassForName('theComponentName', MyComponent);
136
-
137
-    uut.crawl(node);
138
-
139
-    expect(node.data.options).toEqual({
140
-      topBar: {
141
-        leftButtons: [{
142
-          id: 'id',
143
-          font: 'Helvetica'
144
-        }, {
145
-          id: 'id2',
146
-          font: 'Helvetica'
147
-        }],
148
-        rightButtons: [{
149
-          id: 'id3'
150
-        }]
151
-      }
152
-    });
153
-  });
154
-
155
-  it('Components: empty buttons array should not affect static buttons', () => {
156
-    const MyComponent = class {
157
-      static get options() {
158
-        return {
159
-          topBar: {}
160
-        };
161
-      }
162
-    };
163
-
164
-    const passedOptions = {
165
-      topBar: {}
166
-    };
167
-
168
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions } };
169
-    store.setOriginalComponentClassForName('theComponentName', MyComponent);
170
-
171
-    uut.crawl(node);
172
-
173
-    expect(node.data.options).toEqual({
174
-      topBar: {}
175
-    });
176
-  });
177
-
178
-  it('Components: static options with no topBar should not crash', () => {
179
-    const MyComponent = class {
180
-      static get options() {
181
-        return {
182
-
183
-        };
184
-      }
185
-    };
186
-
187
-    const passedOptions = {
188
-      topBar: {}
189
-    };
190
-
191
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions } };
192
-    store.setOriginalComponentClassForName('theComponentName', MyComponent);
193
-
194
-    uut.crawl(node);
195
-
196
-    expect(node.data.options).toEqual({
197
-      topBar: {}
198
-    });
199
-  });
200
-
201
-  it('Components: undefined passed buttons should not affect static buttons', () => {
202
-    const MyComponent = class {
203
-      static get options() {
204
-        return {
205
-          topBar: {
206
-            leftButtons: [{
207
-              id: 'id'
208
-            }],
209
-            rightButtons: [{
210
-              id: 'id2'
211
-            }]
212
-          }
213
-        };
214
-      }
215
-    };
216
-
217
-    const passedOptions = {
218
-      topBar: {}
219
-    };
220
-
221
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions } };
222
-    store.setOriginalComponentClassForName('theComponentName', MyComponent);
223
-
224
-    uut.crawl(node);
225
-
226
-    expect(node.data.options).toEqual({
227
-      topBar: {
228
-        leftButtons: [{
229
-          id: 'id'
230
-        }],
231
-        rightButtons: [{
232
-          id: 'id2'
233
-        }]
234
-      }
235
-    });
236
-  });
237 107
   it('Component: deepClones options', () => {
238 108
     const theStyle = {};
239 109
     const MyComponent = class {

+ 0
- 37
lib/src/commands/LayoutTreeCrawler.ts View File

@@ -54,46 +54,9 @@ export class LayoutTreeCrawler {
54 54
     const clazz = this.store.getOriginalComponentClassForName(node.data.name) || {};
55 55
     const staticOptions = _.cloneDeep(clazz.options) || {};
56 56
     const passedOptions = node.data.options || {};
57
-    this._mergeButtonsStyles(passedOptions, staticOptions);
58 57
     node.data.options = _.merge({}, staticOptions, passedOptions);
59 58
   }
60 59
 
61
-  _mergeButtonsStyles(passedOptions, staticOptions) {
62
-    if (passedOptions.topBar) {
63
-      this._normalizeButtons(passedOptions.topBar.leftButtons, (buttons, style) => {
64
-        passedOptions.topBar.leftButtons = buttons;
65
-
66
-        if (staticOptions.topBar) {
67
-          this._applyButtonsStyle(staticOptions.topBar.leftButtons, style);
68
-        }
69
-      });
70
-
71
-      this._normalizeButtons(passedOptions.topBar.rightButtons, (buttons, style) => {
72
-        passedOptions.topBar.rightButtons = buttons;
73
-
74
-        if (staticOptions.topBar) {
75
-          this._applyButtonsStyle(staticOptions.topBar.rightButtons, style);
76
-        }
77
-      });
78
-    }
79
-  }
80
-
81
-  _normalizeButtons(buttons, callback) {
82
-    if (_.isPlainObject(buttons)) {
83
-      callback([], buttons);
84
-    } else {
85
-      callback(buttons);
86
-    }
87
-  }
88
-
89
-  _applyButtonsStyle(buttons, style) {
90
-    if (_.isArray(buttons)) {
91
-      buttons.forEach((button) => {
92
-        _.merge(button, style);
93
-      });
94
-    }
95
-  }
96
-
97 60
   _assertKnownLayoutType(type) {
98 61
     if (!LayoutType[type]) {
99 62
       throw new Error(`Unknown layout type ${type}`);

+ 2
- 1
playground/src/screens/PushedScreen.js View File

@@ -19,7 +19,8 @@ class PushedScreen extends Component {
19 19
           id: 'singleBtn',
20 20
           text: 'single',
21 21
           testID: testIDs.TOP_BAR_BUTTON
22
-        }
22
+        },
23
+        rightButtonColor: 'red',
23 24
       },
24 25
       layout: {
25 26
         backgroundColor: '#f5fcff'