Browse Source

Support topTabs.height style option

This commit also changes the way TopTabs options are handled. As the TabLayout
is part of the TopBar, it's the StackControllers responsibility to set TopTabs options.
Guy Carmeli 6 years ago
parent
commit
b2a8ac41e5

+ 6
- 6
lib/android/app/src/main/java/com/reactnativenavigation/parse/Options.java View File

@@ -22,7 +22,7 @@ public class Options {
22 22
         if (json == null) return result.withDefaultOptions(defaultOptions);
23 23
 
24 24
         result.topBar = TopBarOptions.parse(typefaceManager, json.optJSONObject("topBar"));
25
-        result.topTabsOptions = TopTabsOptions.parse(json.optJSONObject("topTabs"));
25
+        result.topTabs = TopTabsOptions.parse(json.optJSONObject("topTabs"));
26 26
         result.topTabOptions = TopTabOptions.parse(typefaceManager, json.optJSONObject("topTab"));
27 27
         result.bottomTabOptions = BottomTabOptions.parse(json.optJSONObject("bottomTab"));
28 28
         result.bottomTabsOptions = BottomTabsOptions.parse(json.optJSONObject("bottomTabs"));
@@ -38,7 +38,7 @@ public class Options {
38 38
     }
39 39
 
40 40
     @NonNull public TopBarOptions topBar = new TopBarOptions();
41
-    @NonNull public TopTabsOptions topTabsOptions = new TopTabsOptions();
41
+    @NonNull public TopTabsOptions topTabs = new TopTabsOptions();
42 42
     @NonNull public TopTabOptions topTabOptions = new TopTabOptions();
43 43
     @NonNull public BottomTabOptions bottomTabOptions = new BottomTabOptions();
44 44
     @NonNull public BottomTabsOptions bottomTabsOptions = new BottomTabsOptions();
@@ -58,7 +58,7 @@ public class Options {
58 58
     public Options copy() {
59 59
         Options result = new Options();
60 60
         result.topBar.mergeWith(topBar);
61
-        result.topTabsOptions.mergeWith(topTabsOptions);
61
+        result.topTabs.mergeWith(topTabs);
62 62
         result.topTabOptions.mergeWith(topTabOptions);
63 63
         result.bottomTabOptions.mergeWith(bottomTabOptions);
64 64
         result.bottomTabsOptions.mergeWith(bottomTabsOptions);
@@ -76,7 +76,7 @@ public class Options {
76 76
 	public Options mergeWith(final Options other) {
77 77
         Options result = copy();
78 78
         result.topBar.mergeWith(other.topBar);
79
-        result.topTabsOptions.mergeWith(other.topTabsOptions);
79
+        result.topTabs.mergeWith(other.topTabs);
80 80
         result.topTabOptions.mergeWith(other.topTabOptions);
81 81
         result.bottomTabOptions.mergeWith(other.bottomTabOptions);
82 82
         result.bottomTabsOptions.mergeWith(other.bottomTabsOptions);
@@ -92,7 +92,7 @@ public class Options {
92 92
     Options withDefaultOptions(final Options defaultOptions) {
93 93
         topBar.mergeWithDefault(defaultOptions.topBar);
94 94
         topTabOptions.mergeWithDefault(defaultOptions.topTabOptions);
95
-        topTabsOptions.mergeWithDefault(defaultOptions.topTabsOptions);
95
+        topTabs.mergeWithDefault(defaultOptions.topTabs);
96 96
         bottomTabOptions.mergeWithDefault(defaultOptions.bottomTabOptions);
97 97
         bottomTabsOptions.mergeWithDefault(defaultOptions.bottomTabsOptions);
98 98
         fabOptions.mergeWithDefault(defaultOptions.fabOptions);
@@ -120,7 +120,7 @@ public class Options {
120 120
     }
121 121
 
122 122
     public Options clearTopTabsOptions() {
123
-        topTabsOptions = new TopTabsOptions();
123
+        topTabs = new TopTabsOptions();
124 124
         return this;
125 125
     }
126 126
 

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java View File

@@ -21,6 +21,7 @@ public class TopTabsOptions {
21 21
     @NonNull public Color unselectedTabColor = new NullColor();
22 22
     @NonNull public Number fontSize = new NullNumber();
23 23
     @NonNull public Bool visible = new NullBool();
24
+    @NonNull public Number height = new NullNumber();
24 25
 
25 26
     public static TopTabsOptions parse(@Nullable JSONObject json) {
26 27
         TopTabsOptions result = new TopTabsOptions();
@@ -29,6 +30,7 @@ public class TopTabsOptions {
29 30
         result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
30 31
         result.fontSize = NumberParser.parse(json, "fontSize");
31 32
         result.visible = BoolParser.parse(json, "visible");
33
+        result.height = NumberParser.parse(json, "height");
32 34
         return result;
33 35
     }
34 36
 
@@ -37,6 +39,7 @@ public class TopTabsOptions {
37 39
         if (other.unselectedTabColor.hasValue()) unselectedTabColor = other.unselectedTabColor;
38 40
         if (other.fontSize.hasValue()) fontSize = other.fontSize;
39 41
         if (other.visible.hasValue()) visible = other.visible;
42
+        if (other.height.hasValue()) height = other.height;
40 43
     }
41 44
 
42 45
     void mergeWithDefault(TopTabsOptions defaultOptions) {
@@ -44,5 +47,6 @@ public class TopTabsOptions {
44 47
         if (!unselectedTabColor.hasValue()) unselectedTabColor = defaultOptions.unselectedTabColor;
45 48
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
46 49
         if (!visible.hasValue()) visible = defaultOptions.visible;
50
+        if (!height.hasValue()) height = defaultOptions.height;
47 51
     }
48 52
 }

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

@@ -37,7 +37,7 @@ public class StackOptionsPresenter {
37 37
         applyOrientation(options.layout.orientation);
38 38
         applyButtons(options.topBar.leftButtons, options.topBar.rightButtons);
39 39
         applyTopBarOptions(options.topBar, options.animations, child, options);
40
-        applyTopTabsOptions(options.topTabsOptions);
40
+        applyTopTabsOptions(options.topTabs);
41 41
         applyTopTabOptions(options.topTabOptions);
42 42
     }
43 43
 
@@ -106,6 +106,7 @@ public class StackOptionsPresenter {
106 106
         topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
107 107
         topBar.applyTopTabsFontSize(options.fontSize);
108 108
         topBar.setTopTabsVisible(options.visible.isTrueOrUndefined());
109
+        topBar.setTopTabsHeight(options.height.get(LayoutParams.WRAP_CONTENT));
109 110
     }
110 111
 
111 112
     private void applyTopTabOptions(TopTabOptions topTabOptions) {
@@ -126,7 +127,7 @@ public class StackOptionsPresenter {
126 127
         mergeOrientation(options.layout.orientation);
127 128
         mergeButtons(options.topBar.leftButtons, options.topBar.rightButtons);
128 129
         mergeTopBarOptions(options.topBar, options.animations, child);
129
-        mergeTopTabsOptions(options.topTabsOptions);
130
+        mergeTopTabsOptions(options.topTabs);
130 131
         mergeTopTabOptions(options.topTabOptions);
131 132
     }
132 133
 
@@ -187,6 +188,7 @@ public class StackOptionsPresenter {
187 188
         if (options.selectedTabColor.hasValue() && options.unselectedTabColor.hasValue()) topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
188 189
         if (options.fontSize.hasValue()) topBar.applyTopTabsFontSize(options.fontSize);
189 190
         if (options.visible.hasValue()) topBar.setTopTabsVisible(options.visible.isTrue());
191
+        if (options.height.hasValue()) topBar.setTopTabsHeight(options.height.get(LayoutParams.WRAP_CONTENT));
190 192
     }
191 193
 
192 194
     private void mergeTopTabOptions(TopTabOptions topTabOptions) {

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

@@ -54,7 +54,12 @@ public class StackController extends ParentController<StackLayout> {
54 54
         }
55 55
         applyOnParentController(parentController ->
56 56
                 ((ParentController) parentController).applyChildOptions(
57
-                        this.options.copy().clearTopBarOptions().clearAnimationOptions().clearFabOptions(),
57
+                        this.options.copy()
58
+                                .clearTopBarOptions()
59
+                                .clearAnimationOptions()
60
+                                .clearFabOptions()
61
+                                .clearTopTabOptions()
62
+                                .clearTopTabsOptions(),
58 63
                         child
59 64
                 )
60 65
         );
@@ -71,7 +76,12 @@ public class StackController extends ParentController<StackLayout> {
71 76
         }
72 77
         applyOnParentController(parentController ->
73 78
                 ((ParentController) parentController).mergeChildOptions(
74
-                        options.copy().clearTopBarOptions().clearAnimationOptions().clearFabOptions(),
79
+                        options.copy()
80
+                                .clearTopBarOptions()
81
+                                .clearAnimationOptions()
82
+                                .clearFabOptions()
83
+                                .clearTopTabOptions()
84
+                                .clearTopTabsOptions(),
75 85
                         child
76 86
                 )
77 87
         );

+ 8
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

@@ -1,6 +1,7 @@
1 1
 package com.reactnativenavigation.viewcontrollers.toptabs;
2 2
 
3 3
 import android.app.Activity;
4
+import android.support.annotation.CallSuper;
4 5
 import android.support.annotation.NonNull;
5 6
 import android.view.View;
6 7
 
@@ -84,11 +85,13 @@ public class TopTabsController extends ParentController<TopTabsViewPager> {
84 85
     @Override
85 86
     public void applyChildOptions(Options options, Component child) {
86 87
         super.applyChildOptions(options, child);
87
-        applyOnParentController(parentController -> {
88
-                Options opt = this.options.copy();
89
-                ((ParentController) parentController).applyChildOptions(opt.clearTopTabOptions().clearTopTabsOptions(), child);
90
-            }
91
-        );
88
+        applyOnParentController(parentController -> ((ParentController) parentController).applyChildOptions(this.options.copy(), child));
89
+    }
90
+
91
+    @CallSuper
92
+    public void mergeChildOptions(Options options, Component child) {
93
+        super.mergeChildOptions(options, child);
94
+        applyOnParentController(parentController -> ((ParentController) parentController).applyChildOptions(options.copy(), child));
92 95
     }
93 96
 
94 97
     public void switchToTab(int index) {

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

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
4 4
 import android.content.Context;
5 5
 import android.graphics.Typeface;
6 6
 import android.support.annotation.ColorInt;
7
+import android.support.annotation.NonNull;
7 8
 import android.support.annotation.RestrictTo;
8 9
 import android.support.annotation.VisibleForTesting;
9 10
 import android.support.design.widget.AppBarLayout;
@@ -13,6 +14,7 @@ import android.view.Gravity;
13 14
 import android.view.View;
14 15
 import android.view.ViewGroup;
15 16
 import android.widget.FrameLayout;
17
+import android.widget.LinearLayout;
16 18
 import android.widget.RelativeLayout;
17 19
 import android.widget.TextView;
18 20
 
@@ -49,6 +51,7 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
49 51
     private TopBarAnimator animator;
50 52
     private TopTabs topTabs;
51 53
     private FrameLayout root;
54
+    private LinearLayout content;
52 55
     private StackLayout parentView;
53 56
     private TopBarBackgroundViewController topBarBackgroundViewController;
54 57
     private View border;
@@ -65,18 +68,37 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
65 68
 
66 69
     private void createLayout(ReactViewCreator buttonCreator, TitleBarReactViewCreator titleBarReactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
67 70
         setId(CompatUtils.generateViewId());
68
-        topTabs = new TopTabs(getContext());
69 71
         titleBar = createTitleBar(getContext(), buttonCreator, titleBarReactViewCreator, onClickListener);
70
-        titleBar.setId(CompatUtils.generateViewId());
72
+        topTabs = createTopTabs();
73
+        border = createBorder();
74
+        content = createContentLayout();
75
+
71 76
         root = new FrameLayout(getContext());
72 77
         root.setId(CompatUtils.generateViewId());
73
-        root.addView(titleBar, MATCH_PARENT, WRAP_CONTENT);
74
-        border = createBorder();
78
+        content.addView(titleBar);
79
+        content.addView(topTabs);
80
+        root.addView(content);
75 81
         root.addView(border);
76 82
         addView(root, MATCH_PARENT, WRAP_CONTENT);
77 83
         if (BuildConfig.DEBUG) setContentDescription("TopBar");
78 84
     }
79 85
 
86
+    private LinearLayout createContentLayout() {
87
+        LinearLayout content = new LinearLayout(getContext());
88
+        content.setOrientation(VERTICAL);
89
+        return content;
90
+    }
91
+
92
+    @NonNull
93
+    private TopTabs createTopTabs() {
94
+        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
95
+        lp.addRule(RelativeLayout.BELOW, titleBar.getId());
96
+        TopTabs topTabs = new TopTabs(getContext());
97
+        topTabs.setLayoutParams(lp);
98
+        topTabs.setVisibility(GONE);
99
+        return topTabs;
100
+    }
101
+
80 102
     private View createBorder() {
81 103
         View border = new View(getContext());
82 104
         border.setBackgroundColor(android.graphics.Color.TRANSPARENT);
@@ -87,7 +109,9 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
87 109
     }
88 110
 
89 111
     protected TitleBar createTitleBar(Context context, ReactViewCreator buttonCreator, TitleBarReactViewCreator reactViewCreator, TopBarButtonController.OnClickListener onClickListener) {
90
-        return new TitleBar(context, buttonCreator, reactViewCreator, onClickListener);
112
+        TitleBar titleBar = new TitleBar(context, buttonCreator, reactViewCreator, onClickListener);
113
+        titleBar.setId(CompatUtils.generateViewId());
114
+        return titleBar;
91 115
     }
92 116
 
93 117
     public void setHeight(int height) {
@@ -177,6 +201,12 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
177 201
         topTabs.setVisibility(this, visible);
178 202
     }
179 203
 
204
+    public void setTopTabsHeight(int height) {
205
+        if (topTabs.getLayoutParams().height == height) return;
206
+        topTabs.getLayoutParams().height = height > 0 ? (int) UiUtils.dpToPx(getContext(), height) : height;
207
+        topTabs.setLayoutParams(topTabs.getLayoutParams());
208
+    }
209
+
180 210
     public void setLeftButtons(List<Button> leftButtons) {
181 211
         titleBar.setLeftButtons(leftButtons);
182 212
     }
@@ -194,11 +224,8 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
194 224
     }
195 225
 
196 226
     public void initTopTabs(ViewPager viewPager) {
197
-        topTabs = new TopTabs(getContext());
227
+        topTabs.setVisibility(VISIBLE);
198 228
         topTabs.init(viewPager);
199
-        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
200
-        lp.addRule(RelativeLayout.BELOW, titleBar.getId());
201
-        root.addView(topTabs, lp);
202 229
     }
203 230
 
204 231
     public void enableCollapse(ScrollEventListener scrollEventListener) {

+ 2
- 2
lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java View File

@@ -277,9 +277,9 @@ public class OptionsTest extends BaseTest {
277 277
     @Test
278 278
     public void clear_topTabsOptions() {
279 279
         Options uut = new Options();
280
-        uut.topTabsOptions.fontSize = new Number(666);
280
+        uut.topTabs.fontSize = new Number(666);
281 281
         uut.clearTopTabsOptions();
282
-        assertThat(uut.topTabsOptions.fontSize.hasValue()).isFalse();
282
+        assertThat(uut.topTabs.fontSize.hasValue()).isFalse();
283 283
     }
284 284
 
285 285
     @Test

+ 6
- 6
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java View File

@@ -117,13 +117,13 @@ public class OptionsMergingTest extends BaseTest {
117 117
         verify(topBar, times(0)).applyTopTabsFontSize(any());
118 118
         verify(topBar, times(0)).setTopTabsVisible(anyBoolean());
119 119
 
120
-        options.topTabsOptions.selectedTabColor = new Color(1);
121
-        options.topTabsOptions.unselectedTabColor = new Color(1);
122
-        options.topTabsOptions.fontSize = new Number(1);
123
-        options.topTabsOptions.visible = new Bool(true);
120
+        options.topTabs.selectedTabColor = new Color(1);
121
+        options.topTabs.unselectedTabColor = new Color(1);
122
+        options.topTabs.fontSize = new Number(1);
123
+        options.topTabs.visible = new Bool(true);
124 124
         uut.mergeChildOptions(options, child);
125
-        verify(topBar, times(1)).applyTopTabsColors(options.topTabsOptions.selectedTabColor, options.topTabsOptions.unselectedTabColor);
126
-        verify(topBar, times(1)).applyTopTabsFontSize(options.topTabsOptions.fontSize);
125
+        verify(topBar, times(1)).applyTopTabsColors(options.topTabs.selectedTabColor, options.topTabs.unselectedTabColor);
126
+        verify(topBar, times(1)).applyTopTabsFontSize(options.topTabs.fontSize);
127 127
         verify(topBar, times(1)).setTopTabsVisible(anyBoolean());
128 128
     }
129 129
 

+ 10
- 21
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

@@ -24,7 +24,6 @@ import com.reactnativenavigation.views.toptabs.TopTabsLayoutCreator;
24 24
 import com.reactnativenavigation.views.toptabs.TopTabsViewPager;
25 25
 
26 26
 import org.junit.Test;
27
-import org.mockito.ArgumentCaptor;
28 27
 import org.mockito.Mockito;
29 28
 
30 29
 import java.util.ArrayList;
@@ -41,7 +40,7 @@ public class TopTabsViewControllerTest extends BaseTest {
41 40
 
42 41
     private static final int SIZE = 2;
43 42
 
44
-    private StackController parentController;
43
+    private StackController stack;
45 44
     private TopTabsController uut;
46 45
     private List<ViewController> tabControllers = new ArrayList<>(SIZE);
47 46
     private List<Options> tabOptions = new ArrayList<>(SIZE);
@@ -65,9 +64,9 @@ public class TopTabsViewControllerTest extends BaseTest {
65 64
         uut = spy(new TopTabsController(activity, childRegistry, "componentId", tabControllers, layoutCreator, options));
66 65
         tabControllers.forEach(viewController -> viewController.setParentController(uut));
67 66
 
68
-        parentController = spy(createStackController("stackId"));
69
-        parentController.push(uut, new CommandListenerAdapter());
70
-        uut.setParentController(parentController);
67
+        stack = spy(createStackController("stackId"));
68
+        stack.push(uut, new CommandListenerAdapter());
69
+        uut.setParentController(stack);
71 70
     }
72 71
 
73 72
     @NonNull
@@ -137,7 +136,7 @@ public class TopTabsViewControllerTest extends BaseTest {
137 136
 
138 137
     @Test
139 138
     public void lifecycleMethodsSentWhenSelectedTabChanges() {
140
-        parentController.ensureViewIsCreated();
139
+        stack.ensureViewIsCreated();
141 140
         uut.ensureViewIsCreated();
142 141
         tabControllers.get(0).ensureViewIsCreated();
143 142
         tabControllers.get(1).ensureViewIsCreated();
@@ -157,7 +156,7 @@ public class TopTabsViewControllerTest extends BaseTest {
157 156
 
158 157
     @Test
159 158
     public void lifecycleMethodsSentWhenSelectedPreviouslySelectedTab() {
160
-        parentController.ensureViewIsCreated();
159
+        stack.ensureViewIsCreated();
161 160
         uut.ensureViewIsCreated();
162 161
         uut.onViewAppeared();
163 162
         uut.switchToTab(1);
@@ -171,7 +170,7 @@ public class TopTabsViewControllerTest extends BaseTest {
171 170
 
172 171
     @Test
173 172
     public void setOptionsOfInitialTab() {
174
-        parentController.ensureViewIsCreated();
173
+        stack.ensureViewIsCreated();
175 174
         uut.ensureViewIsCreated();
176 175
         uut.onViewAppeared();
177 176
         verify(tabControllers.get(0), times(1)).onViewAppeared();
@@ -183,7 +182,7 @@ public class TopTabsViewControllerTest extends BaseTest {
183 182
 
184 183
     @Test
185 184
     public void setOptionsWhenTabChanges() {
186
-        parentController.ensureViewIsCreated();
185
+        stack.ensureViewIsCreated();
187 186
         uut.ensureViewIsCreated();
188 187
         tabControllers.get(0).ensureViewIsCreated();
189 188
         tabControllers.get(1).ensureViewIsCreated();
@@ -211,7 +210,7 @@ public class TopTabsViewControllerTest extends BaseTest {
211 210
     @Test
212 211
     public void appliesOptionsOnLayoutWhenVisible() {
213 212
         tabControllers.get(0).ensureViewIsCreated();
214
-        parentController.ensureViewIsCreated();
213
+        stack.ensureViewIsCreated();
215 214
         uut.ensureViewIsCreated();
216 215
 
217 216
         uut.onViewAppeared();
@@ -219,19 +218,9 @@ public class TopTabsViewControllerTest extends BaseTest {
219 218
         verify(topTabsLayout, times(1)).applyOptions(any(Options.class));
220 219
     }
221 220
 
222
-    @Test
223
-    public void applyOptions_applyOnlyOnFirstTopTabs() {
224
-        tabOptions.get(0).topTabOptions.title = new Text("tab title");
225
-        tabControllers.get(0).onViewAppeared();
226
-        ArgumentCaptor<Options> optionsCaptor = ArgumentCaptor.forClass(Options.class);
227
-        ArgumentCaptor<ReactComponent> viewCaptor = ArgumentCaptor.forClass(ReactComponent.class);
228
-        verify(parentController, times(1)).applyChildOptions(optionsCaptor.capture(), viewCaptor.capture());
229
-        assertThat(optionsCaptor.getValue().topTabOptions.title.hasValue()).isFalse();
230
-    }
231
-
232 221
     @Test
233 222
     public void applyOptions_tabsAreRemovedAfterViewDisappears() {
234
-        parentController.getView().removeAllViews();
223
+        stack.getView().removeAllViews();
235 224
 
236 225
         StackController stackController = spy(createStackController("stack"));
237 226
         ComponentViewController first = new ComponentViewController(

+ 0
- 1
playground/src/screens/WelcomeScreen.js View File

@@ -432,7 +432,6 @@ class WelcomeScreen extends Component {
432 432
           topTabs: {
433 433
             selectedTabColor: '#12766b',
434 434
             unselectedTabColor: 'red',
435
-            height: 220,
436 435
             fontSize: 6
437 436
           }
438 437
         }