Browse Source

Implement changing title dynamically on Android #1071 (#1483)

* Upgrade Gradle version

* Allow playground app to run on x86_64 emulator

* #1071 Allow changing navigation title

* #1071 Add tests for JavaScript
Juozas Kontvainis 7 years ago
parent
commit
fd665d71ef

+ 8
- 0
AndroidE2E/app/src/androidTest/java/com/reactnativenavigation/e2e/androide2e/ScreenStyleStaticTest.java View File

11
 		elementByText("PUSH OPTIONS SCREEN").click();
11
 		elementByText("PUSH OPTIONS SCREEN").click();
12
 		assertExists(By.text("Static Title"));
12
 		assertExists(By.text("Static Title"));
13
 	}
13
 	}
14
+
15
+	@Test
16
+	public void setTitleDynamically() throws Exception {
17
+		elementByText("PUSH OPTIONS SCREEN").click();
18
+		assertExists(By.text("Static Title"));
19
+		elementByText("DYNAMIC OPTIONS").click();
20
+		assertExists(By.text("Dynamic Title"));
21
+	}
14
 }
22
 }

+ 12
- 0
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

10
 import com.reactnativenavigation.NavigationActivity;
10
 import com.reactnativenavigation.NavigationActivity;
11
 import com.reactnativenavigation.layout.LayoutFactory;
11
 import com.reactnativenavigation.layout.LayoutFactory;
12
 import com.reactnativenavigation.layout.LayoutNode;
12
 import com.reactnativenavigation.layout.LayoutNode;
13
+import com.reactnativenavigation.layout.NavigationOptions;
13
 import com.reactnativenavigation.parse.JSONParser;
14
 import com.reactnativenavigation.parse.JSONParser;
14
 import com.reactnativenavigation.parse.LayoutNodeParser;
15
 import com.reactnativenavigation.parse.LayoutNodeParser;
15
 import com.reactnativenavigation.utils.UiThread;
16
 import com.reactnativenavigation.utils.UiThread;
42
 		});
43
 		});
43
 	}
44
 	}
44
 
45
 
46
+	@ReactMethod
47
+	public void setOptions(final String onContainerId, final ReadableMap options) {
48
+		final NavigationOptions navOptions = NavigationOptions.parse(JSONParser.parse(options));
49
+		handle(new Runnable() {
50
+			@Override
51
+			public void run() {
52
+				navigator().setOptions(onContainerId, navOptions);
53
+			}
54
+		});
55
+	}
56
+
45
 	@ReactMethod
57
 	@ReactMethod
46
 	public void push(final String onContainerId, final ReadableMap rawLayoutTree) {
58
 	public void push(final String onContainerId, final ReadableMap rawLayoutTree) {
47
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
59
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/Navigator.java View File

5
 import android.view.ViewGroup;
5
 import android.view.ViewGroup;
6
 import android.widget.FrameLayout;
6
 import android.widget.FrameLayout;
7
 
7
 
8
+import com.reactnativenavigation.layout.NavigationOptions;
8
 import com.reactnativenavigation.utils.CompatUtils;
9
 import com.reactnativenavigation.utils.CompatUtils;
9
 
10
 
10
 import java.util.Collection;
11
 import java.util.Collection;
54
 		getView().addView(viewController.getView());
55
 		getView().addView(viewController.getView());
55
 	}
56
 	}
56
 
57
 
58
+	public void setOptions(final String containerId, NavigationOptions options) {
59
+		ViewController target = findControllerById(containerId);
60
+		target.getParentStackController().setTitle(options.title);
61
+	}
62
+
57
 	public void push(final String fromId, final ViewController viewController) {
63
 	public void push(final String fromId, final ViewController viewController) {
58
 		ViewController from = findControllerById(fromId);
64
 		ViewController from = findControllerById(fromId);
59
 		if (from != null) {
65
 		if (from != null) {

+ 4
- 0
lib/src/Navigation.js View File

33
     return this.commands.setRoot(params);
33
     return this.commands.setRoot(params);
34
   }
34
   }
35
 
35
 
36
+  setOptions(containerId, options) {
37
+    this.commands.setOptions(containerId, options);
38
+  }
39
+
36
   showModal(params) {
40
   showModal(params) {
37
     return this.commands.showModal(params);
41
     return this.commands.showModal(params);
38
   }
42
   }

+ 8
- 0
lib/src/Navigation.test.js View File

30
     expect(Navigation.commands.setRoot).toHaveBeenCalledWith(params);
30
     expect(Navigation.commands.setRoot).toHaveBeenCalledWith(params);
31
   });
31
   });
32
 
32
 
33
+  it('setOptions delegates to Commands', async () => {
34
+    const theContainerId = "7";
35
+    const params = { title: "T" };
36
+    Navigation.setOptions(theContainerId, params);
37
+    expect(Navigation.commands.setOptions).toHaveBeenCalledTimes(1);
38
+    expect(Navigation.commands.setOptions).toHaveBeenCalledWith(theContainerId, params);
39
+  });
40
+
33
   it('showModal delegates to Commands', async () => {
41
   it('showModal delegates to Commands', async () => {
34
     Navigation.commands.showModal.mockReturnValue(Promise.resolve('result'));
42
     Navigation.commands.showModal.mockReturnValue(Promise.resolve('result'));
35
     const params = {};
43
     const params = {};

+ 4
- 0
lib/src/adapters/NativeCommandsSender.js View File

10
     return Promise.resolve(layoutTree);
10
     return Promise.resolve(layoutTree);
11
   }
11
   }
12
 
12
 
13
+  setOptions(containerId, options) {
14
+    this.nativeCommandsModule.setOptions(containerId, options);
15
+  }
16
+
13
   push(onContainerId, layout) {
17
   push(onContainerId, layout) {
14
     this.nativeCommandsModule.push(onContainerId, layout);
18
     this.nativeCommandsModule.push(onContainerId, layout);
15
     return Promise.resolve(layout);
19
     return Promise.resolve(layout);

+ 6
- 0
lib/src/adapters/NativeCommandsSender.test.js View File

7
   beforeEach(() => {
7
   beforeEach(() => {
8
     mockNativeModule = {
8
     mockNativeModule = {
9
       setRoot: jest.fn(),
9
       setRoot: jest.fn(),
10
+      setOptions: jest.fn(),
10
       push: jest.fn(),
11
       push: jest.fn(),
11
       pop: jest.fn(),
12
       pop: jest.fn(),
12
       popTo: jest.fn(),
13
       popTo: jest.fn(),
29
     expect(result).toBeDefined();
30
     expect(result).toBeDefined();
30
   });
31
   });
31
 
32
 
33
+  it('setOptions sends to native with containerId and params', () => {
34
+    uut.setOptions('theContainerId', { title: "Title" });
35
+    expect(mockNativeModule.setOptions).toHaveBeenCalledTimes(1);
36
+  });
37
+
32
   it('push sends to native and resolves with promise', async () => {
38
   it('push sends to native and resolves with promise', async () => {
33
     const theNewContainer = {};
39
     const theNewContainer = {};
34
     const result = await uut.push('theContainerId', theNewContainer);
40
     const result = await uut.push('theContainerId', theNewContainer);

+ 5
- 0
lib/src/commands/Commands.js View File

14
     return this.nativeCommandsSender.setRoot(layout);
14
     return this.nativeCommandsSender.setRoot(layout);
15
   }
15
   }
16
 
16
 
17
+  setOptions(containerId, options) {
18
+    const input = _.cloneDeep(options);
19
+    this.nativeCommandsSender.setOptions(containerId, input);
20
+  }
21
+
17
   showModal(simpleApi) {
22
   showModal(simpleApi) {
18
     const input = _.cloneDeep(simpleApi);
23
     const input = _.cloneDeep(simpleApi);
19
     const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
24
     const layout = this.layoutTreeParser.parseFromSimpleJSON(input);

+ 14
- 0
lib/src/commands/Commands.test.js View File

64
     });
64
     });
65
   });
65
   });
66
 
66
 
67
+  describe('setOptions', () => {
68
+    it('deep clones input to avoid mutation errors', () => {
69
+      const obj = { title: "test" };
70
+      uut.setOptions('theContainerId', obj);
71
+      expect(mockCommandsSender.setOptions.mock.calls[0][1]).not.toBe(obj);
72
+    });
73
+
74
+    it('passes options for container', () => {
75
+      uut.setOptions('theContainerId', { title: "1" });
76
+      expect(mockCommandsSender.setOptions).toHaveBeenCalledTimes(1);
77
+      expect(mockCommandsSender.setOptions).toHaveBeenCalledWith('theContainerId', { title: "1" });
78
+    });
79
+  });
80
+
67
   describe('showModal', () => {
81
   describe('showModal', () => {
68
     it('sends command to native after parsing into a correct layout tree', () => {
82
     it('sends command to native after parsing into a correct layout tree', () => {
69
       uut.showModal({
83
       uut.showModal({

+ 3
- 0
playground/android/app/build.gradle View File

17
         targetSdkVersion 25
17
         targetSdkVersion 25
18
         versionCode 1
18
         versionCode 1
19
         versionName "1.0"
19
         versionName "1.0"
20
+        ndk {
21
+            abiFilters "armeabi-v7a", "x86"
22
+        }
20
     }
23
     }
21
     signingConfigs {
24
     signingConfigs {
22
         release {
25
         release {

+ 1
- 1
playground/android/build.gradle View File

5
         jcenter()
5
         jcenter()
6
     }
6
     }
7
     dependencies {
7
     dependencies {
8
-        classpath 'com.android.tools.build:gradle:2.3.2'
8
+        classpath 'com.android.tools.build:gradle:2.3.3'
9
 
9
 
10
         // NOTE: Do not place your application dependencies here; they belong
10
         // NOTE: Do not place your application dependencies here; they belong
11
         // in the individual module build.gradle files
11
         // in the individual module build.gradle files

+ 3
- 3
playground/src/containers/OptionsScreen.js View File

30
   }
30
   }
31
 
31
 
32
   onClickDynamicOptions() {
32
   onClickDynamicOptions() {
33
-    // Navigation.setOptions({
34
-    //   title: 'Dynamic Title'
35
-    // });
33
+    Navigation.setOptions(this.props.containerId, {
34
+      title: 'Dynamic Title'
35
+    });
36
   }
36
   }
37
 }
37
 }
38
 
38