Browse Source

Side menu works

Guy Carmeli 8 years ago
parent
commit
f2fdac7dde

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/layout/Container.java View File

@@ -4,8 +4,8 @@ import android.content.Context;
4 4
 import android.widget.FrameLayout;
5 5
 
6 6
 public class Container extends FrameLayout {
7
-    public Container(Context context, LayoutFactory.RootViewCreator rootViewCreator, String id, String name) {
7
+    public Container(Context context, LayoutFactory.ReactRootViewCreator reactRootViewCreator, String id, String name) {
8 8
         super(context);
9
-        addView(rootViewCreator.createRootView(id, name));
9
+        addView(reactRootViewCreator.create(id, name));
10 10
     }
11 11
 }

android/app/src/main/java/com/reactnativenavigation/layout/ContainerStack.java → android/app/src/main/java/com/reactnativenavigation/layout/ContainerStackLayout.java View File

@@ -3,8 +3,8 @@ package com.reactnativenavigation.layout;
3 3
 import android.content.Context;
4 4
 import android.widget.FrameLayout;
5 5
 
6
-public class ContainerStack extends FrameLayout {
7
-    public ContainerStack(Context context) {
6
+public class ContainerStackLayout extends FrameLayout {
7
+    public ContainerStackLayout(Context context) {
8 8
         super(context);
9 9
     }
10 10
 }

+ 58
- 15
android/app/src/main/java/com/reactnativenavigation/layout/LayoutFactory.java View File

@@ -1,26 +1,30 @@
1 1
 package com.reactnativenavigation.layout;
2 2
 
3 3
 import android.app.Activity;
4
+import android.support.v4.widget.DrawerLayout;
5
+import android.view.Gravity;
4 6
 import android.view.View;
7
+import android.view.ViewGroup.LayoutParams;
5 8
 
6
-import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
7 9
 import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
10
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsLayout;
11
+import com.reactnativenavigation.utils.ViewIdGenerator;
8 12
 
9 13
 import java.util.List;
10 14
 
11 15
 public class LayoutFactory {
12 16
 
13
-    public interface RootViewCreator {
14
-        View createRootView(String id, String name);
17
+    public interface ReactRootViewCreator {
18
+        View create(String id, String name);
15 19
     }
16 20
 
17 21
     private final Activity activity;
18
-    private final RootViewCreator rootViewCreator;
22
+    private final ReactRootViewCreator reactRootViewCreator;
19 23
     private final BottomTabsCreator bottomTabsCreator; // TODO: revisit this, may not be needed
20 24
 
21
-    public LayoutFactory(Activity activity, RootViewCreator rootViewCreator, BottomTabsCreator bottomTabsCreator) {
25
+    public LayoutFactory(Activity activity, ReactRootViewCreator reactRootViewCreator, BottomTabsCreator bottomTabsCreator) {
22 26
         this.activity = activity;
23
-        this.rootViewCreator = rootViewCreator;
27
+        this.reactRootViewCreator = reactRootViewCreator;
24 28
         this.bottomTabsCreator = bottomTabsCreator;
25 29
     }
26 30
 
@@ -32,35 +36,74 @@ public class LayoutFactory {
32 36
                 return createContainerStackView(node);
33 37
             case "BottomTabs":
34 38
                 return createBottomTabs(node);
39
+            case "SideMenuRoot":
40
+                return createSideMenuRoot(node);
41
+            case "SideMenuCenter":
42
+                return createSideMenuContent(node);
43
+            case "SideMenuLeft":
44
+                return createSideMenuLeft(node);
45
+            case "SideMenuRight":
46
+                return createSideMenuRight(node);
35 47
             default:
36 48
                 throw new IllegalArgumentException("Invalid node type: "+node.type);
37 49
         }
38 50
     }
39 51
 
52
+    private View createSideMenuRoot(LayoutNode node) {
53
+        SideMenuLayout sideMenuLayout = new SideMenuLayout(activity);
54
+        for (LayoutNode child : node.children) {
55
+            sideMenuLayout.addView(create(child));
56
+        }
57
+        return sideMenuLayout;
58
+    }
59
+
60
+    private View createSideMenuContent(LayoutNode node) {
61
+        return create(node.children.get(0));
62
+    }
63
+
64
+    private View createSideMenuLeft(LayoutNode node) {
65
+        View view = create(node.children.get(0));
66
+        view.setId(ViewIdGenerator.generate());
67
+        DrawerLayout.LayoutParams lp = new DrawerLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
68
+        lp.gravity = Gravity.LEFT;
69
+        view.setLayoutParams(lp);
70
+        return view;
71
+    }
72
+
73
+    private View createSideMenuRight(LayoutNode node) {
74
+        View view = create(node.children.get(0));
75
+        view.setId(ViewIdGenerator.generate());
76
+        DrawerLayout.LayoutParams lp = new DrawerLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
77
+        lp.gravity = Gravity.RIGHT;
78
+        view.setLayoutParams(lp);
79
+        return view;
80
+    }
81
+
40 82
     private View createContainerView(LayoutNode node) {
41 83
         final String name = (String) node.data.get("name");
42
-        return new Container(activity, rootViewCreator, node.id, name);
84
+        Container container = new Container(activity, reactRootViewCreator, node.id, name);
85
+        container.setId(ViewIdGenerator.generate());
86
+        return container;
87
+
43 88
     }
44 89
 
45 90
     private View createContainerStackView(LayoutNode node) {
46
-        final ContainerStack containerStack = new ContainerStack(activity);
91
+        final ContainerStackLayout containerStack = new ContainerStackLayout(activity);
92
+        containerStack.setId(ViewIdGenerator.generate());
47 93
         addChildrenNodes(containerStack, node.children);
48 94
         return containerStack;
49 95
     }
50 96
 
51 97
     private View createBottomTabs(LayoutNode node) {
52
-        final BottomTabsContainer tabsContainer = new BottomTabsContainer(activity, bottomTabsCreator.create());
53
-
54
-        int i = 0;
55
-        for (LayoutNode child : node.children) {
56
-            final View tabContent = create(child);
98
+        final BottomTabsLayout tabsContainer = new BottomTabsLayout(activity, bottomTabsCreator.create());
99
+        for (int i = 0; i < node.children.size(); i++) {
100
+            final View tabContent = create(node.children.get(i));
57 101
             tabsContainer.addTabContent("#" + i, tabContent);
58
-            i++;
59 102
         }
60 103
         return tabsContainer;
61 104
     }
62 105
 
63
-    private void addChildrenNodes(ContainerStack containerStack, List<LayoutNode> children) {
106
+    private void addChildrenNodes(ContainerStackLayout containerStack, List<LayoutNode> children) {
64 107
         for (LayoutNode child : children) {
65 108
             containerStack.addView(create(child));
66 109
         }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/layout/LayoutNode.java View File

@@ -12,6 +12,11 @@ public class LayoutNode {
12 12
     public LayoutNode() {
13 13
     }
14 14
 
15
+    public LayoutNode(String type, List<LayoutNode> children) {
16
+        this.type = type;
17
+        this.children = children;
18
+    }
19
+
15 20
     public LayoutNode(String id, String type, Map<String, Object> data) {
16 21
         this.id = id;
17 22
         this.type = type;

+ 10
- 0
android/app/src/main/java/com/reactnativenavigation/layout/SideMenuLayout.java View File

@@ -0,0 +1,10 @@
1
+package com.reactnativenavigation.layout;
2
+
3
+import android.content.Context;
4
+import android.support.v4.widget.DrawerLayout;
5
+
6
+public class SideMenuLayout extends DrawerLayout {
7
+    public SideMenuLayout(Context context) {
8
+        super(context);
9
+    }
10
+}

+ 3
- 28
android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabs.java View File

@@ -1,17 +1,14 @@
1 1
 package com.reactnativenavigation.layout.bottomtabs;
2 2
 
3 3
 import android.graphics.Color;
4
-import android.os.Build;
5 4
 import android.support.annotation.NonNull;
6 5
 import android.support.design.widget.BottomNavigationView;
7 6
 import android.view.Menu;
8 7
 import android.view.MenuItem;
9
-import android.view.View;
10 8
 import android.widget.RelativeLayout;
11 9
 import android.widget.RelativeLayout.LayoutParams;
12 10
 
13
-import java.util.concurrent.atomic.AtomicInteger;
14
-
11
+import static android.view.View.generateViewId;
15 12
 import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
16 13
 
17 14
 public class BottomTabs implements BottomNavigationView.OnNavigationItemSelectedListener {
@@ -20,14 +17,12 @@ public class BottomTabs implements BottomNavigationView.OnNavigationItemSelected
20 17
         void onTabSelected(int index);
21 18
     }
22 19
 
23
-    private static final AtomicInteger viewId = new AtomicInteger(1);
24
-
25 20
     private BottomNavigationView bottomNavigationView;
26 21
     private BottomTabsSelectionListener listener;
27 22
 
28 23
     public void attach(RelativeLayout parentLayout) {
29 24
         createBottomNavigation(parentLayout);
30
-        addButtomNavigationToParent(parentLayout);
25
+        addBottomNavigationToParent(parentLayout);
31 26
     }
32 27
 
33 28
     public void setSelectionListener(BottomTabsSelectionListener listener) {
@@ -60,30 +55,10 @@ public class BottomTabs implements BottomNavigationView.OnNavigationItemSelected
60 55
         bottomNavigationView.setOnNavigationItemSelectedListener(this);
61 56
     }
62 57
 
63
-    private void addButtomNavigationToParent(RelativeLayout parentLayout) {
58
+    private void addBottomNavigationToParent(RelativeLayout parentLayout) {
64 59
         LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
65 60
         lp.addRule(ALIGN_PARENT_BOTTOM);
66 61
         bottomNavigationView.setLayoutParams(lp);
67 62
         parentLayout.addView(bottomNavigationView, lp);
68 63
     }
69
-
70
-    private int generateViewId() {
71
-        if (Build.VERSION.SDK_INT >= 17) {
72
-            return View.generateViewId();
73
-        } else {
74
-            return compatGenerateViewId();
75
-        }
76
-    }
77
-
78
-    private int compatGenerateViewId() {
79
-        while(true) {
80
-            final int result = viewId.get();
81
-            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
82
-            int newValue = result + 1;
83
-            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
84
-            if (viewId.compareAndSet(result, newValue)) {
85
-                return result;
86
-            }
87
-        }
88
-    }
89 64
 }

android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsContainer.java → android/app/src/main/java/com/reactnativenavigation/layout/bottomtabs/BottomTabsLayout.java View File

@@ -7,13 +7,13 @@ import android.widget.RelativeLayout;
7 7
 import java.util.ArrayList;
8 8
 import java.util.List;
9 9
 
10
-public class BottomTabsContainer extends RelativeLayout implements BottomTabs.BottomTabsSelectionListener {
10
+public class BottomTabsLayout extends RelativeLayout implements BottomTabs.BottomTabsSelectionListener {
11 11
 
12 12
     private List<View> tabsContent;
13 13
     private BottomTabs bottomTabs;
14 14
     private int currentTab;
15 15
 
16
-    public BottomTabsContainer(Activity activity, BottomTabs bottomTabs) {
16
+    public BottomTabsLayout(Activity activity, BottomTabs bottomTabs) {
17 17
         super(activity);
18 18
         initBottomTabs(bottomTabs);
19 19
     }

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

@@ -36,9 +36,9 @@ public class NavigationModule extends ReactContextBaseJavaModule {
36 36
         NavigationActivity.instance.runOnUiThread(new Runnable() {
37 37
             @Override
38 38
             public void run() {
39
-                LayoutFactory factory = new LayoutFactory(NavigationActivity.instance, new LayoutFactory.RootViewCreator() {
39
+                LayoutFactory factory = new LayoutFactory(NavigationActivity.instance, new LayoutFactory.ReactRootViewCreator() {
40 40
                     @Override
41
-                    public View createRootView(String id, String name) {
41
+                    public View create(String id, String name) {
42 42
                         ReactRootView rootView = new ReactRootView(NavigationActivity.instance);
43 43
                         Bundle opts = new Bundle();
44 44
                         opts.putString("id", id);

+ 30
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ViewIdGenerator.java View File

@@ -0,0 +1,30 @@
1
+package com.reactnativenavigation.utils;
2
+
3
+import android.os.Build;
4
+import android.view.View;
5
+
6
+import java.util.concurrent.atomic.AtomicInteger;
7
+
8
+public class ViewIdGenerator {
9
+    private static final AtomicInteger viewId = new AtomicInteger(1);
10
+
11
+    public static int generate() {
12
+        if (Build.VERSION.SDK_INT >= 17) {
13
+            return View.generateViewId();
14
+        } else {
15
+            return compatGenerateViewId();
16
+        }
17
+    }
18
+
19
+    private static int compatGenerateViewId() {
20
+        while(true) {
21
+            final int result = viewId.get();
22
+            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
23
+            int newValue = result + 1;
24
+            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
25
+            if (viewId.compareAndSet(result, newValue)) {
26
+                return result;
27
+            }
28
+        }
29
+    }
30
+}

+ 12
- 12
android/app/src/test/java/com/reactnativenavigation/BottomTabsContainerTest.java View File

@@ -4,7 +4,7 @@ import android.app.Activity;
4 4
 import android.view.View;
5 5
 
6 6
 import com.reactnativenavigation.layout.bottomtabs.BottomTabs;
7
-import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
7
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsLayout;
8 8
 import com.reactnativenavigation.layout.bottomtabs.TooManyTabsException;
9 9
 
10 10
 import org.junit.Before;
@@ -36,7 +36,7 @@ public class BottomTabsContainerTest {
36 36
     public void addsTabToBottomTabs() throws Exception {
37 37
         View tabContent = new View(activity);
38 38
 
39
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
39
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
40 40
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
41 41
 
42 42
         verify(bottomTabs).add(TAB_NAME);
@@ -46,7 +46,7 @@ public class BottomTabsContainerTest {
46 46
     public void addsTabContentToLayout() throws Exception {
47 47
         View tabContent = new View(activity);
48 48
 
49
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
49
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
50 50
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
51 51
 
52 52
         verify(bottomTabs).attach(bottomTabsContainer);
@@ -58,7 +58,7 @@ public class BottomTabsContainerTest {
58 58
         View tabContent = new View(activity);
59 59
         View otherTabContent = new View(activity);
60 60
 
61
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
61
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
62 62
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
63 63
         bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
64 64
 
@@ -67,7 +67,7 @@ public class BottomTabsContainerTest {
67 67
 
68 68
     @Test (expected = TooManyTabsException.class)
69 69
     public void throwsExceptionWhenMoreThenFiveTabs() throws Exception {
70
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
70
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
71 71
         for (int i = 0; i < 6; i++) {
72 72
             View content = new View(activity);
73 73
             bottomTabsContainer.addTabContent("#" + i, content);
@@ -76,7 +76,7 @@ public class BottomTabsContainerTest {
76 76
 
77 77
     @Test
78 78
     public void addFiveTabs() throws Exception {
79
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
79
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
80 80
         for (int i = 0; i < 5; i++) {
81 81
             View content = new View(activity);
82 82
             bottomTabsContainer.addTabContent("#" + i, content);
@@ -88,7 +88,7 @@ public class BottomTabsContainerTest {
88 88
         View tabContent = new View(activity);
89 89
         View otherTabContent = new View(activity);
90 90
 
91
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
91
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
92 92
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
93 93
         bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
94 94
 
@@ -98,7 +98,7 @@ public class BottomTabsContainerTest {
98 98
 
99 99
     @Test
100 100
     public void listensToTabsSwitchingEvents() throws Exception {
101
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
101
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
102 102
         verify(bottomTabs).setSelectionListener(bottomTabsContainer);
103 103
     }
104 104
 
@@ -107,7 +107,7 @@ public class BottomTabsContainerTest {
107 107
         View tabContent = new View(activity);
108 108
         View otherTabContent = new View(activity);
109 109
 
110
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
110
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
111 111
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
112 112
         bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
113 113
         bottomTabsContainer.onTabSelected(1);
@@ -121,7 +121,7 @@ public class BottomTabsContainerTest {
121 121
         View tabContent = new View(activity);
122 122
         View otherTabContent = new View(activity);
123 123
 
124
-        BottomTabsContainer bottomTabsContainer = createBottomTabsContainer();
124
+        BottomTabsLayout bottomTabsContainer = createBottomTabsContainer();
125 125
         bottomTabsContainer.addTabContent(TAB_NAME, tabContent);
126 126
         bottomTabsContainer.addTabContent(OTHER_TAB_NAME, otherTabContent);
127 127
         bottomTabsContainer.onTabSelected(1);
@@ -131,7 +131,7 @@ public class BottomTabsContainerTest {
131 131
         assertThat(otherTabContent.getVisibility()).isEqualTo(View.GONE);
132 132
     }
133 133
 
134
-    private BottomTabsContainer createBottomTabsContainer() {
135
-        return new BottomTabsContainer(activity, bottomTabs);
134
+    private BottomTabsLayout createBottomTabsContainer() {
135
+        return new BottomTabsLayout(activity, bottomTabs);
136 136
     }
137 137
 }

+ 69
- 39
android/app/src/test/java/com/reactnativenavigation/LayoutFactoryTest.java View File

@@ -6,12 +6,13 @@ import android.view.View;
6 6
 import android.view.ViewGroup;
7 7
 
8 8
 import com.reactnativenavigation.layout.Container;
9
-import com.reactnativenavigation.layout.ContainerStack;
9
+import com.reactnativenavigation.layout.ContainerStackLayout;
10 10
 import com.reactnativenavigation.layout.LayoutFactory;
11 11
 import com.reactnativenavigation.layout.LayoutNode;
12
+import com.reactnativenavigation.layout.SideMenuLayout;
12 13
 import com.reactnativenavigation.layout.bottomtabs.BottomTabs;
13
-import com.reactnativenavigation.layout.bottomtabs.BottomTabsContainer;
14 14
 import com.reactnativenavigation.layout.bottomtabs.BottomTabsCreator;
15
+import com.reactnativenavigation.layout.bottomtabs.BottomTabsLayout;
15 16
 
16 17
 import org.junit.Before;
17 18
 import org.junit.Test;
@@ -33,28 +34,28 @@ import static org.mockito.Mockito.when;
33 34
 @RunWith(RobolectricTestRunner.class)
34 35
 public class LayoutFactoryTest {
35 36
 
36
-    private final static String VIEW_ID = "myUniqueId";
37
-    private final static String VIEW_NAME = "myName";
37
+    private final static String NODE_ID = "myUniqueId";
38
+    private final static String REACT_ROOT_VIEW_KEY = "myName";
38 39
 
39
-    private final static String OTHER_VIEW_ID = "anotherUniqueId";
40
-    private final static String OTHER_VIEW_NAME = "anotherName";
40
+    private final static String OTHER_NODE_ID = "anotherUniqueId";
41
+    private final static String OTHER_REACT_ROOT_VIEW_KEY = "anotherName";
41 42
 
42 43
     private Activity activity;
43 44
     private View mockView;
44 45
     private View otherMockView;
45
-    private LayoutFactory.RootViewCreator rootViewCreator;
46
+    private LayoutFactory.ReactRootViewCreator reactRootViewCreator;
46 47
 
47 48
     @Before
48 49
     public void setUp() {
49 50
         activity = Robolectric.buildActivity(AppCompatActivity.class).get();
50 51
         mockView = new View(activity);
51 52
         otherMockView = new View(activity);
52
-        rootViewCreator = mock(LayoutFactory.RootViewCreator.class);
53
+        reactRootViewCreator = mock(LayoutFactory.ReactRootViewCreator.class);
53 54
     }
54 55
 
55 56
     @Test
56 57
     public void returnsContainerThatHoldsTheRootView() throws Exception {
57
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
58
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
58 59
         final LayoutNode node = createContainerNode();
59 60
 
60 61
         final ViewGroup result = (ViewGroup) createLayoutFactory().create(node);
@@ -65,13 +66,13 @@ public class LayoutFactoryTest {
65 66
 
66 67
     @Test
67 68
     public void returnsContainerStack() throws Exception {
68
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
69
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
69 70
         final LayoutNode containerNode = createContainerNode();
70 71
         final LayoutNode stackNode = createContainerStackNode(containerNode);
71 72
 
72 73
         final ViewGroup result = (ViewGroup) createLayoutFactory().create(stackNode);
73 74
 
74
-        assertThat(result).isInstanceOf(ContainerStack.class);
75
+        assertThat(result).isInstanceOf(ContainerStackLayout.class);
75 76
         ViewGroup container = (ViewGroup) TestUtils.assertViewChildrenCount(result, 1).get(0);
76 77
         TestUtils.assertViewChildren(container, mockView);
77 78
     }
@@ -80,16 +81,16 @@ public class LayoutFactoryTest {
80 81
     public void returnsContainerStackWithMultipleViews() throws Exception {
81 82
         final View mockView1 = mock(View.class);
82 83
         final View mockView2 = mock(View.class);
83
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView1);
84
-        when(rootViewCreator.createRootView(eq(OTHER_VIEW_ID), eq(OTHER_VIEW_NAME))).thenReturn(mockView2);
84
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView1);
85
+        when(reactRootViewCreator.create(eq(OTHER_NODE_ID), eq(OTHER_REACT_ROOT_VIEW_KEY))).thenReturn(mockView2);
85 86
 
86
-        final LayoutNode containerNode1 = createContainerNode(VIEW_ID, VIEW_NAME);
87
-        final LayoutNode containerNode2 = createContainerNode(OTHER_VIEW_ID, OTHER_VIEW_NAME);
87
+        final LayoutNode containerNode1 = createContainerNode(NODE_ID, REACT_ROOT_VIEW_KEY);
88
+        final LayoutNode containerNode2 = createContainerNode(OTHER_NODE_ID, OTHER_REACT_ROOT_VIEW_KEY);
88 89
         final LayoutNode stackNode = createContainerStackNode(containerNode1, containerNode2);
89 90
 
90 91
         final ViewGroup result = (ViewGroup) createLayoutFactory().create(stackNode);
91 92
 
92
-        assertThat(result).isInstanceOf(ContainerStack.class);
93
+        assertThat(result).isInstanceOf(ContainerStackLayout.class);
93 94
         List<View> containers = TestUtils.assertViewChildrenCount(result, 2);
94 95
         ViewGroup container1 = (ViewGroup) containers.get(0);
95 96
         ViewGroup container2 = (ViewGroup) containers.get(1);
@@ -97,21 +98,47 @@ public class LayoutFactoryTest {
97 98
         TestUtils.assertViewChildren(container2, mockView2);
98 99
     }
99 100
 
101
+    @Test
102
+    public void returnsSideMenuRoot() throws Exception {
103
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
104
+        final LayoutNode containerNode = createSideMenuContainerNode(Arrays.asList(createContainerNode()));
105
+        final ViewGroup result = (ViewGroup) createLayoutFactory().create(containerNode);
106
+        assertThat(result).isInstanceOf(SideMenuLayout.class);
107
+    }
108
+
109
+    @Test
110
+    public void hasContentContainer() throws Exception {
111
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
112
+        LayoutNode contentContainer = createContainerNode();
113
+        final LayoutNode sideMenu = createSideMenuContainerNode(Arrays.asList(contentContainer));
114
+        final ViewGroup result = (ViewGroup) createLayoutFactory().create(sideMenu);
115
+        assertThat(result.getChildAt(0)).isInstanceOf(Container.class);
116
+    }
117
+
118
+    @Test
119
+    public void hasLeftMenu() throws Exception {
120
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
121
+        LayoutNode sideMenuLeft = createSideMenuLeftNode();
122
+        final LayoutNode sideMenu = createSideMenuContainerNode(Arrays.asList(sideMenuLeft));
123
+        final ViewGroup result = (ViewGroup) createLayoutFactory().create(sideMenu);
124
+        assertThat(result.getChildAt(0)).isInstanceOf(Container.class);
125
+    }
126
+
100 127
     @Test
101 128
     public void returnsSingleTabContent() throws Exception {
102 129
         BottomTabs bottomTabsMock = mock(BottomTabs.class);
103 130
         when(bottomTabsMock.size()).thenReturn(0);
104 131
 
105
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
132
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
106 133
         final LayoutNode containerNode = createContainerNode();
107
-        final LayoutNode tabNode = createTabNode(containerNode);
134
+        final LayoutNode tabNode = createBottomTabNode(containerNode);
108 135
 
109 136
         final View result = createLayoutFactory(bottomTabsMock).create(tabNode);
110 137
 
111 138
         verify(bottomTabsMock).add("#0");
112 139
 
113
-        assertThat(result).isInstanceOf(BottomTabsContainer.class);
114
-        Container container = (Container) TestUtils.assertViewChildrenCount((BottomTabsContainer) result, 1).get(0);
140
+        assertThat(result).isInstanceOf(BottomTabsLayout.class);
141
+        Container container = (Container) TestUtils.assertViewChildrenCount((BottomTabsLayout) result, 1).get(0);
115 142
         View view = TestUtils.assertViewChildrenCount(container, 1).get(0);
116 143
         assertThat(view).isEqualTo(mockView);
117 144
     }
@@ -121,17 +148,17 @@ public class LayoutFactoryTest {
121 148
         BottomTabs bottomTabsMock = mock(BottomTabs.class);
122 149
         when(bottomTabsMock.size()).thenReturn(0, 1);
123 150
 
124
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
125
-        final LayoutNode firstTabRootNode = createContainerNode(VIEW_ID, VIEW_NAME);
151
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
152
+        final LayoutNode firstTabRootNode = createContainerNode(NODE_ID, REACT_ROOT_VIEW_KEY);
126 153
 
127
-        when(rootViewCreator.createRootView(eq(OTHER_VIEW_ID), eq(OTHER_VIEW_NAME))).thenReturn(otherMockView);
128
-        final LayoutNode secondTabRootNode = createContainerStackNode(createContainerNode(OTHER_VIEW_ID, OTHER_VIEW_NAME));
154
+        when(reactRootViewCreator.create(eq(OTHER_NODE_ID), eq(OTHER_REACT_ROOT_VIEW_KEY))).thenReturn(otherMockView);
155
+        final LayoutNode secondTabRootNode = createContainerStackNode(createContainerNode(OTHER_NODE_ID, OTHER_REACT_ROOT_VIEW_KEY));
129 156
 
130
-        final LayoutNode tabNode = createTabNode(firstTabRootNode, secondTabRootNode);
157
+        final LayoutNode tabNode = createBottomTabNode(firstTabRootNode, secondTabRootNode);
131 158
 
132 159
         final View result = createLayoutFactory(bottomTabsMock).create(tabNode);
133 160
 
134
-        assertThat(result).isInstanceOf(BottomTabsContainer.class);
161
+        assertThat(result).isInstanceOf(BottomTabsLayout.class);
135 162
         verify(bottomTabsMock).add(eq("#0"));
136 163
         verify(bottomTabsMock).add(eq("#1"));
137 164
     }
@@ -139,8 +166,8 @@ public class LayoutFactoryTest {
139 166
 
140 167
     @Test(expected = IllegalArgumentException.class)
141 168
     public void throwsExceptionForUnknownType() throws Exception {
142
-        when(rootViewCreator.createRootView(eq(VIEW_ID), eq(VIEW_NAME))).thenReturn(mockView);
143
-        final LayoutNode node = new LayoutNode(VIEW_ID, "***unknownType***", Collections.<String, Object>emptyMap());
169
+        when(reactRootViewCreator.create(eq(NODE_ID), eq(REACT_ROOT_VIEW_KEY))).thenReturn(mockView);
170
+        final LayoutNode node = new LayoutNode(NODE_ID, "***unknownType***", Collections.<String, Object>emptyMap());
144 171
 
145 172
         createLayoutFactory().create(node);
146 173
     }
@@ -156,28 +183,31 @@ public class LayoutFactoryTest {
156 183
             when(bottomTabsCreator.create()).thenReturn(bottomTabs);
157 184
         }
158 185
 
159
-        return new LayoutFactory(activity, rootViewCreator, bottomTabsCreator);
186
+        return new LayoutFactory(activity, reactRootViewCreator, bottomTabsCreator);
160 187
     }
161 188
 
162 189
     private LayoutNode createContainerNode() {
163
-        return createContainerNode(VIEW_ID, VIEW_NAME);
190
+        return createContainerNode(NODE_ID, REACT_ROOT_VIEW_KEY);
191
+    }
192
+
193
+    private LayoutNode createSideMenuLeftNode() {
194
+        List<LayoutNode> children = Arrays.asList(createContainerNode());
195
+        return new LayoutNode("SideMenuLeft", children);
164 196
     }
165 197
 
166 198
     private LayoutNode createContainerNode(final String id, final String name) {
167 199
         return new LayoutNode(id, "Container", new HashMap<String, Object>() {{ put("name", name); }});
168 200
     }
169 201
 
202
+    private LayoutNode createSideMenuContainerNode(List<LayoutNode> children) {
203
+        return new LayoutNode("SideMenuRoot", children);
204
+    }
205
+
170 206
     private LayoutNode createContainerStackNode(LayoutNode... children) {
171
-        LayoutNode node = new LayoutNode();
172
-        node.type = "ContainerStack";
173
-        node.children = Arrays.asList(children);
174
-        return node;
207
+        return new LayoutNode("ContainerStack", Arrays.asList(children));
175 208
     }
176 209
 
177
-    private LayoutNode createTabNode(LayoutNode... children) {
178
-        LayoutNode node = new LayoutNode();
179
-        node.type = "BottomTabs";
180
-        node.children = Arrays.asList(children);
181
-        return node;
210
+    private LayoutNode createBottomTabNode(LayoutNode... children) {
211
+        return new LayoutNode("BottomTabs", Arrays.asList(children));
182 212
     }
183 213
 }