Daniel Zlotin 7 years ago
parent
commit
e354e21500

+ 1
- 0
.eslintrc View File

@@ -3,6 +3,7 @@
3 3
     "es6": true,
4 4
     "node": true
5 5
   },
6
+  "parser": "babel-eslint",
6 7
   "parserOptions": {
7 8
     "ecmaVersion": 8,
8 9
     "sourceType": "module",

+ 11
- 0
e2e/app.test.js View File

@@ -186,6 +186,17 @@ describe('reload app', async () => {
186 186
   });
187 187
 });
188 188
 
189
+describe('screen style - static', () => {
190
+  beforeEach(async () => {
191
+    await device.relaunchApp();
192
+  });
193
+
194
+  it('declare a static style object on container component', async () => {
195
+    await elementByLabel('Push').tap();
196
+    await expect(elementByLabel('Static Title')).toBeVisible();
197
+  });
198
+});
199
+
189 200
 function elementByLabel(label) {
190 201
   return element(by.label(label));
191 202
 }

+ 1
- 1
lib/src/commands/AppCommands.js View File

@@ -16,7 +16,7 @@ export default class AppCommands {
16 16
 
17 17
   showModal(simpleApi) {
18 18
     const input = _.cloneDeep(simpleApi);
19
-    const layout = this.layoutTreeParser.createContainer(input);
19
+    const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
20 20
     this.layoutTreeCrawler.crawl(layout);
21 21
     return this.nativeCommandsSender.showModal(layout);
22 22
   }

+ 23
- 12
lib/src/commands/AppCommands.test.js View File

@@ -37,7 +37,8 @@ describe('AppCommands', () => {
37 37
             id: 'Container+UNIQUE_ID',
38 38
             children: [],
39 39
             data: {
40
-              name: 'com.example.MyScreen'
40
+              name: 'com.example.MyScreen',
41
+              navigationStyle: {}
41 42
             }
42 43
           }
43 44
         ]
@@ -46,7 +47,7 @@ describe('AppCommands', () => {
46 47
 
47 48
     it('deep clones input to avoid mutation errors', () => {
48 49
       const obj = {};
49
-      uut.setRoot({ container: { inner: obj } });
50
+      uut.setRoot({ container: { name: 'bla', inner: obj } });
50 51
       expect(mockCommandsSender.setRoot.mock.calls[0][0].children[0].data.inner).not.toBe(obj);
51 52
     });
52 53
 
@@ -66,30 +67,40 @@ describe('AppCommands', () => {
66 67
   describe('showModal', () => {
67 68
     it('sends command to native after parsing into a correct layout tree', () => {
68 69
       uut.showModal({
69
-        name: 'com.example.MyScreen'
70
+        container: {
71
+          name: 'com.example.MyScreen'
72
+        }
70 73
       });
71 74
       expect(mockCommandsSender.showModal).toHaveBeenCalledTimes(1);
72 75
       expect(mockCommandsSender.showModal).toHaveBeenCalledWith({
73
-        type: 'Container',
74
-        id: 'Container+UNIQUE_ID',
75
-        children: [],
76
-        data: {
77
-          name: 'com.example.MyScreen'
78
-        }
76
+        type: "ContainerStack",
77
+        id: "ContainerStack+UNIQUE_ID",
78
+        data: {},
79
+        children: [{
80
+          type: "Container",
81
+          id: "Container+UNIQUE_ID",
82
+          data: {
83
+            name: "com.example.MyScreen",
84
+            navigationStyle: {}
85
+          },
86
+          children: []
87
+        }]
79 88
       });
80 89
     });
81 90
 
82 91
     it('deep clones input to avoid mutation errors', () => {
83 92
       const obj = {};
84
-      uut.showModal({ inner: obj });
93
+      uut.showModal({ container: { name: 'name', inner: obj } });
85 94
       expect(mockCommandsSender.showModal.mock.calls[0][0].data.inner).not.toBe(obj);
86 95
     });
87 96
 
88 97
     it('passProps into containers', () => {
89 98
       expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({});
90 99
       uut.showModal({
91
-        name: 'com.example.MyScreen',
92
-        passProps: SimpleLayouts.passProps
100
+        container: {
101
+          name: 'com.example.MyScreen',
102
+          passProps: SimpleLayouts.passProps
103
+        }
93 104
       });
94 105
       expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual(SimpleLayouts.passProps);
95 106
     });

+ 5
- 2
lib/src/commands/ContainerCommands.test.js View File

@@ -23,7 +23,7 @@ describe('ContainerCommands', () => {
23 23
   describe('push', () => {
24 24
     it('deep clones input to avoid mutation errors', () => {
25 25
       const obj = {};
26
-      uut.push({ inner: { foo: obj } });
26
+      uut.push({ name: 'name', inner: { foo: obj } });
27 27
       expect(mockCommandsSender.push.mock.calls[0][1].data.inner.foo).not.toBe(obj);
28 28
     });
29 29
 
@@ -39,7 +39,10 @@ describe('ContainerCommands', () => {
39 39
       expect(mockCommandsSender.push).toHaveBeenCalledWith('myUniqueId', {
40 40
         type: 'Container',
41 41
         id: 'Container+UNIQUE_ID',
42
-        data: { name: 'com.example.MyScreen' },
42
+        data: {
43
+          name: 'com.example.MyScreen',
44
+          navigationStyle: {}
45
+        },
43 46
         children: []
44 47
       });
45 48
     });

+ 14
- 1
lib/src/commands/LayoutTreeCrawler.js View File

@@ -14,14 +14,27 @@ export default class LayoutTreeCrawler {
14 14
     node.data = node.data || {};
15 15
     node.children = node.children || [];
16 16
     if (node.type === LayoutTypes.Container) {
17
-      this.store.setPropsForContainerId(node.id, node.data.passProps);
17
+      this._handleContainer(node);
18 18
     }
19 19
     _.forEach(node.children, this.crawl);
20 20
   }
21 21
 
22
+  _handleContainer(node) {
23
+    this._assertContainerDataName(node);
24
+    this.store.setPropsForContainerId(node.id, node.data.passProps);
25
+    const clazz = this.store.getOriginalContainerClassForName(node.data.name);
26
+    node.data.navigationStyle = _.cloneDeep(_.get(clazz, 'navigationStyle', {}));
27
+  }
28
+
22 29
   _assertKnownLayoutType(type) {
23 30
     if (!_.includes(LayoutTypes, type)) {
24 31
       throw new Error(`Unknown layout type ${type}`);
25 32
     }
26 33
   }
34
+
35
+  _assertContainerDataName(container) {
36
+    if (!container.data.name) {
37
+      throw new Error('Missing container data.name');
38
+    }
39
+  }
27 40
 }

+ 47
- 6
lib/src/commands/LayoutTreeCrawler.test.js View File

@@ -13,34 +13,75 @@ describe('LayoutTreeCrawler', () => {
13 13
   });
14 14
 
15 15
   it('crawls a layout tree and adds unique id to each node', () => {
16
-    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.BottomTabs }] };
16
+    const node = { type: LayoutTypes.ContainerStack, children: [{ type: LayoutTypes.BottomTabs }] };
17 17
     uut.crawl(node);
18
-    expect(node.id).toEqual('Container+UNIQUE_ID');
18
+    expect(node.id).toEqual('ContainerStack+UNIQUE_ID');
19 19
     expect(node.children[0].id).toEqual('BottomTabs+UNIQUE_ID');
20 20
   });
21 21
 
22 22
   it('crawls a layout tree and ensures data exists', () => {
23
-    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.BottomTabs }] };
23
+    const node = { type: LayoutTypes.ContainerStack, children: [{ type: LayoutTypes.BottomTabs }] };
24 24
     uut.crawl(node);
25 25
     expect(node.data).toEqual({});
26 26
     expect(node.children[0].data).toEqual({});
27 27
   });
28 28
 
29 29
   it('crawls a layout tree and ensures children exists', () => {
30
-    const node = { type: LayoutTypes.Container, children: [{ type: LayoutTypes.BottomTabs }] };
30
+    const node = { type: LayoutTypes.ContainerStack, children: [{ type: LayoutTypes.BottomTabs }] };
31 31
     uut.crawl(node);
32 32
     expect(node.children[0].children).toEqual([]);
33 33
   });
34 34
 
35 35
   it('crawls a layout tree and asserts known layout type', () => {
36
-    const node = { type: LayoutTypes.Container, children: [{ type: 'Bob' }] };
36
+    const node = { type: LayoutTypes.ContainerStack, children: [{ type: 'Bob' }] };
37 37
     expect(() => uut.crawl(node)).toThrow(new Error('Unknown layout type Bob'));
38 38
   });
39 39
 
40 40
   it('saves passProps into store for Container nodes', () => {
41
-    const node = { type: LayoutTypes.BottomTabs, children: [{ type: LayoutTypes.Container, data: { passProps: { myProp: 123 } } }] };
41
+    const node = {
42
+      type: LayoutTypes.BottomTabs, children: [
43
+        { type: LayoutTypes.Container, data: { name: 'the name', passProps: { myProp: 123 } } }]
44
+    };
42 45
     expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({});
43 46
     uut.crawl(node);
44 47
     expect(store.getPropsForContainerId('Container+UNIQUE_ID')).toEqual({ myProp: 123 });
45 48
   });
49
+
50
+  it('Containers: injects navigationStyle from original container class static property', () => {
51
+    const theStyle = {};
52
+    const MyContainer = class {
53
+      static navigationStyle = theStyle;
54
+    };
55
+
56
+    const node = { type: LayoutTypes.Container, data: { name: 'theContainerName' } };
57
+    store.setOriginalContainerClassForName('theContainerName', MyContainer);
58
+    uut.crawl(node);
59
+    expect(node.data.navigationStyle).toEqual(theStyle);
60
+  });
61
+
62
+  it('Containers: deepClones navigationStyle', () => {
63
+    const theStyle = {};
64
+    const MyContainer = class {
65
+      static navigationStyle = theStyle;
66
+    };
67
+
68
+    const node = { type: LayoutTypes.Container, data: { name: 'theContainerName' } };
69
+    store.setOriginalContainerClassForName('theContainerName', MyContainer);
70
+    uut.crawl(node);
71
+    expect(node.data.navigationStyle).not.toBe(theStyle);
72
+  });
73
+
74
+  it('Containers: must contain data name', () => {
75
+    const node = { type: LayoutTypes.Container, data: {} };
76
+    expect(() => uut.crawl(node)).toThrow(new Error('Missing container data.name'));
77
+  });
78
+
79
+  it('Containers: navigationStyle default obj', () => {
80
+    const MyContainer = class { };
81
+
82
+    const node = { type: LayoutTypes.Container, data: { name: 'theContainerName' } };
83
+    store.setOriginalContainerClassForName('theContainerName', MyContainer);
84
+    uut.crawl(node);
85
+    expect(node.data.navigationStyle).toEqual({});
86
+  });
46 87
 });

+ 1
- 1
lib/src/containers/ContainerRegistry.js View File

@@ -9,7 +9,7 @@ export default class ContainerRegistry {
9 9
   registerContainer(containerName, getContainerFunc) {
10 10
     const OriginalContainer = getContainerFunc();
11 11
     const NavigationContainer = ContainerWrapper.wrap(containerName, OriginalContainer, this.store);
12
-    this.store.setContainerClassForName(containerName, NavigationContainer);
12
+    this.store.setOriginalContainerClassForName(containerName, OriginalContainer);
13 13
     AppRegistry.registerComponent(containerName, () => NavigationContainer);
14 14
   }
15 15
 }

+ 4
- 4
lib/src/containers/ContainerRegistry.test.js View File

@@ -28,12 +28,12 @@ describe('ContainerRegistry', () => {
28 28
     expect(AppRegistry.registerComponent.mock.calls[0][0]).toEqual('example.MyContainer.name');
29 29
   });
30 30
 
31
-  it('saves the container wrapped into the store', () => {
32
-    expect(store.getContainerClassForName('example.MyContainer.name')).toBeUndefined();
31
+  it('saves the original container into the store', () => {
32
+    expect(store.getOriginalContainerClassForName('example.MyContainer.name')).toBeUndefined();
33 33
     uut.registerContainer('example.MyContainer.name', () => MyContainer);
34
-    const Class = store.getContainerClassForName('example.MyContainer.name');
34
+    const Class = store.getOriginalContainerClassForName('example.MyContainer.name');
35 35
     expect(Class).not.toBeUndefined();
36
-    expect(Class).not.toEqual(MyContainer);
36
+    expect(Class).toEqual(MyContainer);
37 37
     expect(Object.getPrototypeOf(Class)).toEqual(Component);
38 38
   });
39 39
 

+ 2
- 2
lib/src/containers/Store.js View File

@@ -15,11 +15,11 @@ export default class Store {
15 15
     return _.get(this.propsByContainerId, containerId, {});
16 16
   }
17 17
 
18
-  setContainerClassForName(containerName, ContainerClass) {
18
+  setOriginalContainerClassForName(containerName, ContainerClass) {
19 19
     _.set(this.containersByName, containerName, ContainerClass);
20 20
   }
21 21
 
22
-  getContainerClassForName(containerName) {
22
+  getOriginalContainerClassForName(containerName) {
23 23
     return _.get(this.containersByName, containerName);
24 24
   }
25 25
 

+ 3
- 3
lib/src/containers/Store.test.js View File

@@ -22,12 +22,12 @@ describe('Store', () => {
22 22
     expect(uut.getPropsForContainerId('container1')).toEqual({});
23 23
   });
24 24
 
25
-  it('holds containers classes by containerName', () => {
25
+  it('holds original containers classes by containerName', () => {
26 26
     const MyComponent = class {
27 27
       //
28 28
     };
29
-    uut.setContainerClassForName('example.mycontainer', MyComponent);
30
-    expect(uut.getContainerClassForName('example.mycontainer')).toEqual(MyComponent);
29
+    uut.setOriginalContainerClassForName('example.mycontainer', MyComponent);
30
+    expect(uut.getOriginalContainerClassForName('example.mycontainer')).toEqual(MyComponent);
31 31
   });
32 32
 
33 33
   it('holds container refs by id', () => {

+ 1
- 0
package.json View File

@@ -53,6 +53,7 @@
53 53
     "babel-cli": "6.x.x",
54 54
     "babel-core": "6.x.x",
55 55
     "babel-polyfill": "6.x.x",
56
+    "babel-eslint": "7.x.x",
56 57
     "babel-preset-react-native": "1.x.x",
57 58
     "babel-register": "6.x.x",
58 59
     "babel-jest": "18.x.x",

+ 4
- 0
playground/src/containers/PushedScreen.js View File

@@ -10,6 +10,10 @@ import {
10 10
 import Navigation from 'react-native-navigation';
11 11
 
12 12
 class PushedScreen extends Component {
13
+  static navigationStyle = {
14
+    title: 'Static Title'
15
+  }
16
+
13 17
   constructor(props) {
14 18
     super(props);
15 19
     this.onClickPush = this.onClickPush.bind(this);

+ 3
- 3
wallaby.js View File

@@ -13,14 +13,14 @@ module.exports = function (wallaby) {
13 13
 
14 14
     files: [
15 15
       'package.json',
16
-      'src/**/*.js',
17
-      '!src/**/*.test.js',
16
+      'lib/src/**/*.js',
17
+      '!lib/src/**/*.test.js',
18 18
       'integration/**/*.js',
19 19
       '!integration/**/*.test.js'
20 20
     ],
21 21
 
22 22
     tests: [
23
-      'src/**/*.test.js',
23
+      'lib/src/**/*.test.js',
24 24
       'integration/**/*.test.js'
25 25
     ],
26 26
 

+ 1
- 1
yarn.lock View File

@@ -279,7 +279,7 @@ babel-core@6.x.x, babel-core@^6.0.0, babel-core@^6.21.0, babel-core@^6.23.0, bab
279 279
     slash "^1.0.0"
280 280
     source-map "^0.5.0"
281 281
 
282
-babel-eslint@7.1.1:
282
+babel-eslint@7.1.1, babel-eslint@7.x.x:
283 283
   version "7.1.1"
284 284
   resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.1.tgz#8a6a884f085aa7060af69cfc77341c2f99370fb2"
285 285
   dependencies: