Explorar el Código

Road to noImplicitAny part 6 (FINAL part) (#4508)

## What this PR is about?
Finally all strict rules are enables for TypeScript compiler! 🎉 Basically previously `noImplicitAny` was turned off but now it plus all strict rules are turned ON! 💋 

## Some highlights of the changes
- `Element` renamed internally to `SharedElement` to make more sense what is it
- `LayoutTreeParser`'s responsibility is now to do all `id` related stuff. Previously it was spread between `LayoutTreeCrawler` and `LayoutTreeParse` so it is more simple now
- clean up a lot of tests because they were testing duplicate stuff that was covered by other tests already
- removed all usages of `static get options` and replaces them with `static options`. This is how it is in the docs plus you cannot have `static get options(passProps)` because it is impossible to have getter with parameters.
Henrik Raitasola hace 6 años
padre
commit
08f8581b3f
Se han modificado 31 ficheros con 450 adiciones y 504 borrados
  1. 7
    8
      lib/src/Navigation.ts
  2. 8
    3
      lib/src/adapters/SharedElement.tsx
  3. 0
    5
      lib/src/adapters/UniqueIdProvider.mock.ts
  4. 228
    232
      lib/src/commands/Commands.test.ts
  5. 88
    157
      lib/src/commands/LayoutTreeCrawler.test.ts
  6. 28
    21
      lib/src/commands/LayoutTreeCrawler.ts
  7. 39
    29
      lib/src/commands/LayoutTreeParser.test.ts
  8. 18
    11
      lib/src/commands/LayoutTreeParser.ts
  9. 0
    1
      lib/src/commands/OptionsProcessor.test.ts
  10. 2
    2
      lib/src/components/ComponentWrapper.test.tsx
  11. 8
    8
      lib/src/components/Store.ts
  12. 2
    2
      lib/src/events/CommandsObserver.ts
  13. 4
    2
      lib/src/events/ComponentEventsObserver.ts
  14. 1
    1
      lib/src/events/EventsRegistry.test.tsx
  15. 1
    1
      playground/src/screens/BackHandlerModalScreen.js
  16. 1
    1
      playground/src/screens/BackHandlerScreen.js
  17. 1
    1
      playground/src/screens/CustomDialog.js
  18. 1
    1
      playground/src/screens/CustomTransitionDestination.js
  19. 1
    1
      playground/src/screens/CustomTransitionOrigin.js
  20. 1
    1
      playground/src/screens/KeyboardScreen.js
  21. 1
    1
      playground/src/screens/ModalScreen.js
  22. 1
    1
      playground/src/screens/OptionsScreen.js
  23. 1
    1
      playground/src/screens/PushedScreen.js
  24. 1
    1
      playground/src/screens/ScrollViewScreen.js
  25. 1
    1
      playground/src/screens/SearchScreen.js
  26. 1
    1
      playground/src/screens/TextScreen.js
  27. 1
    1
      playground/src/screens/TopTabOptionsScreen.js
  28. 1
    1
      playground/src/screens/TopTabScreen.js
  29. 1
    1
      playground/src/screens/WelcomeScreen.js
  30. 1
    1
      playground/src/screens/complexlayouts/BottomTabSideMenuScreen.js
  31. 1
    6
      tsconfig-strict.json

+ 7
- 8
lib/src/Navigation.ts Ver fichero

@@ -9,7 +9,7 @@ import { LayoutTreeParser } from './commands/LayoutTreeParser';
9 9
 import { LayoutTreeCrawler } from './commands/LayoutTreeCrawler';
10 10
 import { EventsRegistry } from './events/EventsRegistry';
11 11
 import { ComponentProvider } from 'react-native';
12
-import { Element } from './adapters/Element';
12
+import { SharedElement } from './adapters/SharedElement';
13 13
 import { CommandsObserver } from './events/CommandsObserver';
14 14
 import { Constants } from './adapters/Constants';
15 15
 import { ComponentEventsObserver } from './events/ComponentEventsObserver';
@@ -23,9 +23,10 @@ import { AssetService } from './adapters/AssetResolver';
23 23
 import { AppRegistryService } from './adapters/AppRegistryService';
24 24
 
25 25
 export class NavigationRoot {
26
-  public readonly Element: React.ComponentType<{ elementId: any; resizeMode?: any; }>;
27
-  public readonly TouchablePreview: React.ComponentType<any>;
28
-  public readonly store: Store;
26
+  public readonly Element = SharedElement;
27
+  public readonly TouchablePreview = TouchablePreview;
28
+
29
+  private readonly store: Store;
29 30
   private readonly nativeEventsReceiver: NativeEventsReceiver;
30 31
   private readonly uniqueIdProvider: UniqueIdProvider;
31 32
   private readonly componentRegistry: ComponentRegistry;
@@ -39,8 +40,6 @@ export class NavigationRoot {
39 40
   private readonly componentWrapper: ComponentWrapper;
40 41
 
41 42
   constructor() {
42
-    this.Element = Element;
43
-    this.TouchablePreview = TouchablePreview;
44 43
     this.componentWrapper = new ComponentWrapper();
45 44
     this.store = new Store();
46 45
     this.nativeEventsReceiver = new NativeEventsReceiver();
@@ -53,9 +52,9 @@ export class NavigationRoot {
53 52
       this.componentWrapper,
54 53
       appRegistryService
55 54
     );
56
-    this.layoutTreeParser = new LayoutTreeParser();
55
+    this.layoutTreeParser = new LayoutTreeParser(this.uniqueIdProvider);
57 56
     const optionsProcessor = new OptionsProcessor(this.store, this.uniqueIdProvider, new ColorService(), new AssetService());
58
-    this.layoutTreeCrawler = new LayoutTreeCrawler(this.uniqueIdProvider, this.store, optionsProcessor);
57
+    this.layoutTreeCrawler = new LayoutTreeCrawler(this.store, optionsProcessor);
59 58
     this.nativeCommandsSender = new NativeCommandsSender();
60 59
     this.commandsObserver = new CommandsObserver(this.uniqueIdProvider);
61 60
     this.commands = new Commands(

lib/src/adapters/Element.tsx → lib/src/adapters/SharedElement.tsx Ver fichero

@@ -2,7 +2,12 @@ import * as React from 'react';
2 2
 import * as PropTypes from 'prop-types';
3 3
 import { requireNativeComponent } from 'react-native';
4 4
 
5
-export class Element extends React.Component<{ elementId: string; resizeMode?: string }> {
5
+export interface SharedElementProps {
6
+  elementId: string;
7
+  resizeMode: string;
8
+}
9
+
10
+export class SharedElement extends React.Component<SharedElementProps> {
6 11
   static propTypes = {
7 12
     elementId: PropTypes.string.isRequired,
8 13
     resizeMode: PropTypes.string
@@ -13,10 +18,10 @@ export class Element extends React.Component<{ elementId: string; resizeMode?: s
13 18
   };
14 19
 
15 20
   render() {
16
-    return <RNNElement {...this.props} />;
21
+    return <RnnSharedElement {...this.props} />;
17 22
   }
18 23
 }
19 24
 
20
-const RNNElement = requireNativeComponent('RNNElement', Element, {
25
+const RnnSharedElement = requireNativeComponent('RNNElement', SharedElement, {
21 26
   nativeOnly: { nativeID: true }
22 27
 });

+ 0
- 5
lib/src/adapters/UniqueIdProvider.mock.ts Ver fichero

@@ -1,5 +0,0 @@
1
-export class UniqueIdProvider {
2
-  generate(prefix: string): string {
3
-    return `${prefix}+UNIQUE_ID`;
4
-  }
5
-}

+ 228
- 232
lib/src/commands/Commands.test.ts Ver fichero

@@ -4,34 +4,37 @@ import { mock, verify, instance, deepEqual, when, anything, anyString } from 'ts
4 4
 import { LayoutTreeParser } from './LayoutTreeParser';
5 5
 import { LayoutTreeCrawler } from './LayoutTreeCrawler';
6 6
 import { Store } from '../components/Store';
7
-import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock';
8 7
 import { Commands } from './Commands';
9 8
 import { CommandsObserver } from '../events/CommandsObserver';
10 9
 import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
11 10
 import { OptionsProcessor } from './OptionsProcessor';
11
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
12
+import { Options } from '../interfaces/Options';
12 13
 
13 14
 describe('Commands', () => {
14 15
   let uut: Commands;
15 16
   let mockedNativeCommandsSender: NativeCommandsSender;
16
-  let nativeCommandsSender: NativeCommandsSender;
17
-  let store: Store;
17
+  let mockedStore: Store;
18 18
   let commandsObserver: CommandsObserver;
19
+  let mockedUniqueIdProvider: UniqueIdProvider;
19 20
 
20 21
   beforeEach(() => {
21
-    store = new Store();
22
-    commandsObserver = new CommandsObserver(new UniqueIdProvider());
23 22
     mockedNativeCommandsSender = mock(NativeCommandsSender);
24
-    nativeCommandsSender = instance(mockedNativeCommandsSender);
23
+    mockedUniqueIdProvider = mock(UniqueIdProvider);
24
+    when(mockedUniqueIdProvider.generate(anything())).thenCall((prefix) => `${prefix}+UNIQUE_ID`);
25
+    const uniqueIdProvider = instance(mockedUniqueIdProvider);
26
+    mockedStore = mock(Store);
27
+    commandsObserver = new CommandsObserver(uniqueIdProvider);
25 28
 
26 29
     const mockedOptionsProcessor = mock(OptionsProcessor);
27 30
     const optionsProcessor = instance(mockedOptionsProcessor);
28 31
 
29 32
     uut = new Commands(
30
-      nativeCommandsSender,
31
-      new LayoutTreeParser(),
32
-      new LayoutTreeCrawler(new UniqueIdProvider(), store, optionsProcessor),
33
+      instance(mockedNativeCommandsSender),
34
+      new LayoutTreeParser(uniqueIdProvider),
35
+      new LayoutTreeCrawler(instance(mockedStore), optionsProcessor),
33 36
       commandsObserver,
34
-      new UniqueIdProvider(),
37
+      uniqueIdProvider,
35 38
       optionsProcessor
36 39
     );
37 40
   });
@@ -39,146 +42,119 @@ describe('Commands', () => {
39 42
   describe('setRoot', () => {
40 43
     it('sends setRoot to native after parsing into a correct layout tree', () => {
41 44
       uut.setRoot({
42
-        root: {
43
-          component: {
44
-            name: 'com.example.MyScreen'
45
-          }
46
-        }
45
+        root: { component: { name: 'com.example.MyScreen' } }
47 46
       });
48
-      verify(mockedNativeCommandsSender.setRoot('setRoot+UNIQUE_ID', deepEqual({
49
-        root: {
50
-          type: 'Component',
51
-          id: 'Component+UNIQUE_ID',
52
-          children: [],
53
-          data: {
54
-            name: 'com.example.MyScreen',
55
-            options: {},
56
-            passProps: undefined
57
-          }
58
-        },
59
-        modals: [],
60
-        overlays: []
61
-      }))).called();
62
-    });
63
-
64
-    it('passProps into components', () => {
65
-      const passProps = {
66
-        fn: () => 'Hello'
67
-      };
68
-      expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual({});
69
-      uut.setRoot({ root: { component: { name: 'asd', passProps } } });
70
-      expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual(passProps);
71
-      expect(store.getPropsForId('Component+UNIQUE_ID').fn()).toEqual('Hello');
47
+      verify(
48
+        mockedNativeCommandsSender.setRoot(
49
+          'setRoot+UNIQUE_ID',
50
+          deepEqual({
51
+            root: {
52
+              type: 'Component',
53
+              id: 'Component+UNIQUE_ID',
54
+              children: [],
55
+              data: { name: 'com.example.MyScreen', options: {}, passProps: undefined }
56
+            },
57
+            modals: [],
58
+            overlays: []
59
+          })
60
+        )
61
+      ).called();
72 62
     });
73 63
 
74 64
     it('returns a promise with the resolved layout', async () => {
75
-      when(mockedNativeCommandsSender.setRoot(anything(), anything())).thenResolve('the resolved layout' as any);
65
+      when(mockedNativeCommandsSender.setRoot(anything(), anything())).thenResolve(
66
+        'the resolved layout'
67
+      );
76 68
       const result = await uut.setRoot({ root: { component: { name: 'com.example.MyScreen' } } });
77 69
       expect(result).toEqual('the resolved layout');
78 70
     });
79 71
 
80 72
     it('inputs modals and overlays', () => {
81 73
       uut.setRoot({
82
-        root: {
83
-          component: {
84
-            name: 'com.example.MyScreen'
85
-          }
86
-        },
87
-        modals: [
88
-          {
89
-            component: {
90
-              name: 'com.example.MyModal'
91
-            }
92
-          }
93
-        ],
94
-        overlays: [
95
-          {
96
-            component: {
97
-              name: 'com.example.MyOverlay'
98
-            }
99
-          }
100
-        ]
74
+        root: { component: { name: 'com.example.MyScreen' } },
75
+        modals: [{ component: { name: 'com.example.MyModal' } }],
76
+        overlays: [{ component: { name: 'com.example.MyOverlay' } }]
101 77
       });
102
-      verify(mockedNativeCommandsSender.setRoot('setRoot+UNIQUE_ID', deepEqual({
103
-        root:
104
-          {
105
-            type: 'Component',
106
-            id: 'Component+UNIQUE_ID',
107
-            children: [],
108
-            data: {
109
-              name: 'com.example.MyScreen',
110
-              options: {},
111
-              passProps: undefined
112
-            }
113
-          },
114
-        modals: [
115
-          {
116
-            type: 'Component',
117
-            id: 'Component+UNIQUE_ID',
118
-            children: [],
119
-            data: {
120
-              name: 'com.example.MyModal',
121
-              options: {},
122
-              passProps: undefined
123
-            }
124
-          }
125
-        ],
126
-        overlays: [
127
-          {
128
-            type: 'Component',
129
-            id: 'Component+UNIQUE_ID',
130
-            children: [],
131
-            data: {
132
-              name: 'com.example.MyOverlay',
133
-              options: {},
134
-              passProps: undefined
135
-            }
136
-          }
137
-        ]
138
-      }))).called();
78
+      verify(
79
+        mockedNativeCommandsSender.setRoot(
80
+          'setRoot+UNIQUE_ID',
81
+          deepEqual({
82
+            root: {
83
+              type: 'Component',
84
+              id: 'Component+UNIQUE_ID',
85
+              children: [],
86
+              data: {
87
+                name: 'com.example.MyScreen',
88
+                options: {},
89
+                passProps: undefined
90
+              }
91
+            },
92
+            modals: [
93
+              {
94
+                type: 'Component',
95
+                id: 'Component+UNIQUE_ID',
96
+                children: [],
97
+                data: {
98
+                  name: 'com.example.MyModal',
99
+                  options: {},
100
+                  passProps: undefined
101
+                }
102
+              }
103
+            ],
104
+            overlays: [
105
+              {
106
+                type: 'Component',
107
+                id: 'Component+UNIQUE_ID',
108
+                children: [],
109
+                data: {
110
+                  name: 'com.example.MyOverlay',
111
+                  options: {},
112
+                  passProps: undefined
113
+                }
114
+              }
115
+            ]
116
+          })
117
+        )
118
+      ).called();
139 119
     });
140 120
   });
141 121
 
142 122
   describe('mergeOptions', () => {
143 123
     it('passes options for component', () => {
144
-      uut.mergeOptions('theComponentId', { title: '1' } as any);
145
-      verify(mockedNativeCommandsSender.mergeOptions('theComponentId', deepEqual({title: '1'}))).called();
124
+      uut.mergeOptions('theComponentId', { blurOnUnmount: true });
125
+      verify(
126
+        mockedNativeCommandsSender.mergeOptions(
127
+          'theComponentId',
128
+          deepEqual({ blurOnUnmount: true })
129
+        )
130
+      ).called();
146 131
     });
147 132
   });
148 133
 
149 134
   describe('showModal', () => {
150 135
     it('sends command to native after parsing into a correct layout tree', () => {
151
-      uut.showModal({
152
-        component: {
153
-          name: 'com.example.MyScreen'
154
-        }
155
-      });
156
-      verify(mockedNativeCommandsSender.showModal('showModal+UNIQUE_ID', deepEqual({
157
-        type: 'Component',
158
-        id: 'Component+UNIQUE_ID',
159
-        data: {
160
-          name: 'com.example.MyScreen',
161
-          options: {},
162
-          passProps: undefined
163
-        },
164
-        children: []
165
-      }))).called();
166
-    });
167
-
168
-    it('passProps into components', () => {
169
-      const passProps = {};
170
-      expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual({});
171
-      uut.showModal({
172
-        component: {
173
-          name: 'com.example.MyScreen',
174
-          passProps
175
-        }
176
-      });
177
-      expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual(passProps);
136
+      uut.showModal({ component: { name: 'com.example.MyScreen' } });
137
+      verify(
138
+        mockedNativeCommandsSender.showModal(
139
+          'showModal+UNIQUE_ID',
140
+          deepEqual({
141
+            type: 'Component',
142
+            id: 'Component+UNIQUE_ID',
143
+            data: {
144
+              name: 'com.example.MyScreen',
145
+              options: {},
146
+              passProps: undefined
147
+            },
148
+            children: []
149
+          })
150
+        )
151
+      ).called();
178 152
     });
179 153
 
180 154
     it('returns a promise with the resolved layout', async () => {
181
-      when(mockedNativeCommandsSender.showModal(anything(), anything())).thenResolve('the resolved layout' as any);
155
+      when(mockedNativeCommandsSender.showModal(anything(), anything())).thenResolve(
156
+        'the resolved layout'
157
+      );
182 158
       const result = await uut.showModal({ component: { name: 'com.example.MyScreen' } });
183 159
       expect(result).toEqual('the resolved layout');
184 160
     });
@@ -187,11 +163,19 @@ describe('Commands', () => {
187 163
   describe('dismissModal', () => {
188 164
     it('sends command to native', () => {
189 165
       uut.dismissModal('myUniqueId', {});
190
-      verify(mockedNativeCommandsSender.dismissModal('dismissModal+UNIQUE_ID', 'myUniqueId', deepEqual({}))).called();
166
+      verify(
167
+        mockedNativeCommandsSender.dismissModal(
168
+          'dismissModal+UNIQUE_ID',
169
+          'myUniqueId',
170
+          deepEqual({})
171
+        )
172
+      ).called();
191 173
     });
192 174
 
193 175
     it('returns a promise with the id', async () => {
194
-      when(mockedNativeCommandsSender.dismissModal(anyString(), anything(), anything())).thenResolve('the id' as any);
176
+      when(
177
+        mockedNativeCommandsSender.dismissModal(anyString(), anything(), anything())
178
+      ).thenResolve('the id');
195 179
       const result = await uut.dismissModal('myUniqueId');
196 180
       expect(result).toEqual('the id');
197 181
     });
@@ -200,11 +184,15 @@ describe('Commands', () => {
200 184
   describe('dismissAllModals', () => {
201 185
     it('sends command to native', () => {
202 186
       uut.dismissAllModals({});
203
-      verify(mockedNativeCommandsSender.dismissAllModals('dismissAllModals+UNIQUE_ID', deepEqual({}))).called();
187
+      verify(
188
+        mockedNativeCommandsSender.dismissAllModals('dismissAllModals+UNIQUE_ID', deepEqual({}))
189
+      ).called();
204 190
     });
205 191
 
206 192
     it('returns a promise with the id', async () => {
207
-      when(mockedNativeCommandsSender.dismissAllModals(anyString(), anything())).thenResolve('the id' as any);
193
+      when(mockedNativeCommandsSender.dismissAllModals(anyString(), anything())).thenResolve(
194
+        'the id'
195
+      );
208 196
       const result = await uut.dismissAllModals();
209 197
       expect(result).toEqual('the id');
210 198
     });
@@ -212,55 +200,53 @@ describe('Commands', () => {
212 200
 
213 201
   describe('push', () => {
214 202
     it('resolves with the parsed layout', async () => {
215
-      when(mockedNativeCommandsSender.push(anyString(), anyString(), anything())).thenResolve('the resolved layout' as any);
216
-      const result = await uut.push('theComponentId', { component: { name: 'com.example.MyScreen' } });
203
+      when(mockedNativeCommandsSender.push(anyString(), anyString(), anything())).thenResolve(
204
+        'the resolved layout'
205
+      );
206
+      const result = await uut.push('theComponentId', {
207
+        component: { name: 'com.example.MyScreen' }
208
+      });
217 209
       expect(result).toEqual('the resolved layout');
218 210
     });
219 211
 
220 212
     it('parses into correct layout node and sends to native', () => {
221 213
       uut.push('theComponentId', { component: { name: 'com.example.MyScreen' } });
222
-      verify(mockedNativeCommandsSender.push('push+UNIQUE_ID', 'theComponentId', deepEqual({
223
-        type: 'Component',
224
-        id: 'Component+UNIQUE_ID',
225
-        data: {
226
-          name: 'com.example.MyScreen',
227
-          options: {},
228
-          passProps: undefined
229
-        },
230
-        children: []
231
-      }))).called();
232
-    });
233
-
234
-    it('calls component generator once', async () => {
235
-      const generator = jest.fn(() => {
236
-        return {};
237
-      });
238
-      store.setComponentClassForName('theComponentName', generator);
239
-      await uut.push('theComponentId', { component: { name: 'theComponentName' } });
240
-      expect(generator).toHaveBeenCalledTimes(1);
214
+      verify(
215
+        mockedNativeCommandsSender.push(
216
+          'push+UNIQUE_ID',
217
+          'theComponentId',
218
+          deepEqual({
219
+            type: 'Component',
220
+            id: 'Component+UNIQUE_ID',
221
+            data: {
222
+              name: 'com.example.MyScreen',
223
+              options: {},
224
+              passProps: undefined
225
+            },
226
+            children: []
227
+          })
228
+        )
229
+      ).called();
241 230
     });
242 231
   });
243 232
 
244 233
   describe('pop', () => {
245 234
     it('pops a component, passing componentId', () => {
246 235
       uut.pop('theComponentId', {});
247
-      verify(mockedNativeCommandsSender.pop('pop+UNIQUE_ID', 'theComponentId', deepEqual({}))).called();
236
+      verify(
237
+        mockedNativeCommandsSender.pop('pop+UNIQUE_ID', 'theComponentId', deepEqual({}))
238
+      ).called();
248 239
     });
249 240
     it('pops a component, passing componentId and options', () => {
250
-      const options = {
251
-        customTransition: {
252
-          animations: [
253
-            { type: 'sharedElement', fromId: 'title2', toId: 'title1', startDelay: 0, springVelocity: 0.2, duration: 0.5 }
254
-          ],
255
-          duration: 0.8
256
-        }
257
-      };
258
-      uut.pop('theComponentId', options as any);
241
+      const options: Options = { popGesture: true };
242
+      uut.pop('theComponentId', options);
259 243
       verify(mockedNativeCommandsSender.pop('pop+UNIQUE_ID', 'theComponentId', options)).called();
260 244
     });
261 245
 
262 246
     it('pop returns a promise that resolves to componentId', async () => {
263
-      when(mockedNativeCommandsSender.pop(anyString(), anyString(), anything())).thenResolve('theComponentId' as any);
247
+      when(mockedNativeCommandsSender.pop(anyString(), anyString(), anything())).thenResolve(
248
+        'theComponentId'
249
+      );
264 250
       const result = await uut.pop('theComponentId', {});
265 251
       expect(result).toEqual('theComponentId');
266 252
     });
@@ -269,11 +255,15 @@ describe('Commands', () => {
269 255
   describe('popTo', () => {
270 256
     it('pops all components until the passed Id is top', () => {
271 257
       uut.popTo('theComponentId', {});
272
-      verify(mockedNativeCommandsSender.popTo('popTo+UNIQUE_ID', 'theComponentId', deepEqual({}))).called();
258
+      verify(
259
+        mockedNativeCommandsSender.popTo('popTo+UNIQUE_ID', 'theComponentId', deepEqual({}))
260
+      ).called();
273 261
     });
274 262
 
275 263
     it('returns a promise that resolves to targetId', async () => {
276
-      when(mockedNativeCommandsSender.popTo(anyString(), anyString(), anything())).thenResolve('theComponentId' as any);
264
+      when(mockedNativeCommandsSender.popTo(anyString(), anyString(), anything())).thenResolve(
265
+        'theComponentId'
266
+      );
277 267
       const result = await uut.popTo('theComponentId');
278 268
       expect(result).toEqual('theComponentId');
279 269
     });
@@ -282,11 +272,15 @@ describe('Commands', () => {
282 272
   describe('popToRoot', () => {
283 273
     it('pops all components to root', () => {
284 274
       uut.popToRoot('theComponentId', {});
285
-      verify(mockedNativeCommandsSender.popToRoot('popToRoot+UNIQUE_ID', 'theComponentId', deepEqual({}))).called();
275
+      verify(
276
+        mockedNativeCommandsSender.popToRoot('popToRoot+UNIQUE_ID', 'theComponentId', deepEqual({}))
277
+      ).called();
286 278
     });
287 279
 
288 280
     it('returns a promise that resolves to targetId', async () => {
289
-      when(mockedNativeCommandsSender.popToRoot(anyString(), anyString(), anything())).thenResolve('theComponentId' as any);
281
+      when(mockedNativeCommandsSender.popToRoot(anyString(), anyString(), anything())).thenResolve(
282
+        'theComponentId'
283
+      );
290 284
       const result = await uut.popToRoot('theComponentId');
291 285
       expect(result).toEqual('theComponentId');
292 286
     });
@@ -295,42 +289,51 @@ describe('Commands', () => {
295 289
   describe('setStackRoot', () => {
296 290
     it('parses into correct layout node and sends to native', () => {
297 291
       uut.setStackRoot('theComponentId', [{ component: { name: 'com.example.MyScreen' } }]);
298
-      verify(mockedNativeCommandsSender.setStackRoot('setStackRoot+UNIQUE_ID', 'theComponentId', deepEqual([
299
-        {
300
-          type: 'Component',
301
-          id: 'Component+UNIQUE_ID',
302
-          data: {
303
-            name: 'com.example.MyScreen',
304
-            options: {},
305
-            passProps: undefined
306
-          },
307
-          children: []
308
-        }
309
-      ]))).called();
292
+      verify(
293
+        mockedNativeCommandsSender.setStackRoot(
294
+          'setStackRoot+UNIQUE_ID',
295
+          'theComponentId',
296
+          deepEqual([
297
+            {
298
+              type: 'Component',
299
+              id: 'Component+UNIQUE_ID',
300
+              data: {
301
+                name: 'com.example.MyScreen',
302
+                options: {},
303
+                passProps: undefined
304
+              },
305
+              children: []
306
+            }
307
+          ])
308
+        )
309
+      ).called();
310 310
     });
311 311
   });
312 312
 
313 313
   describe('showOverlay', () => {
314 314
     it('sends command to native after parsing into a correct layout tree', () => {
315
-      uut.showOverlay({
316
-        component: {
317
-          name: 'com.example.MyScreen'
318
-        }
319
-      });
320
-      verify(mockedNativeCommandsSender.showOverlay('showOverlay+UNIQUE_ID', deepEqual({
321
-        type: 'Component',
322
-        id: 'Component+UNIQUE_ID',
323
-        data: {
324
-          name: 'com.example.MyScreen',
325
-          options: {},
326
-          passProps: undefined
327
-        },
328
-        children: []
329
-      }))).called();
315
+      uut.showOverlay({ component: { name: 'com.example.MyScreen' } });
316
+      verify(
317
+        mockedNativeCommandsSender.showOverlay(
318
+          'showOverlay+UNIQUE_ID',
319
+          deepEqual({
320
+            type: 'Component',
321
+            id: 'Component+UNIQUE_ID',
322
+            data: {
323
+              name: 'com.example.MyScreen',
324
+              options: {},
325
+              passProps: undefined
326
+            },
327
+            children: []
328
+          })
329
+        )
330
+      ).called();
330 331
     });
331 332
 
332 333
     it('resolves with the component id', async () => {
333
-      when(mockedNativeCommandsSender.showOverlay(anyString(), anything())).thenResolve('Component1' as any);
334
+      when(mockedNativeCommandsSender.showOverlay(anyString(), anything())).thenResolve(
335
+        'Component1'
336
+      );
334 337
       const result = await uut.showOverlay({ component: { name: 'com.example.MyScreen' } });
335 338
       expect(result).toEqual('Component1');
336 339
     });
@@ -338,7 +341,7 @@ describe('Commands', () => {
338 341
 
339 342
   describe('dismissOverlay', () => {
340 343
     it('check promise returns true', async () => {
341
-      when(mockedNativeCommandsSender.dismissOverlay(anyString(), anyString())).thenResolve(true as any);
344
+      when(mockedNativeCommandsSender.dismissOverlay(anyString(), anyString())).thenResolve(true);
342 345
       const result = await uut.dismissOverlay('Component1');
343 346
       verify(mockedNativeCommandsSender.dismissOverlay(anyString(), anyString())).called();
344 347
       expect(result).toEqual(true);
@@ -346,27 +349,36 @@ describe('Commands', () => {
346 349
 
347 350
     it('send command to native with componentId', () => {
348 351
       uut.dismissOverlay('Component1');
349
-      verify(mockedNativeCommandsSender.dismissOverlay('dismissOverlay+UNIQUE_ID', 'Component1')).called();
352
+      verify(
353
+        mockedNativeCommandsSender.dismissOverlay('dismissOverlay+UNIQUE_ID', 'Component1')
354
+      ).called();
350 355
     });
351 356
   });
352 357
 
353 358
   describe('notifies commandsObserver', () => {
354 359
     let cb: any;
360
+    let mockedLayoutTreeParser: LayoutTreeParser;
361
+    let mockedLayoutTreeCrawler: LayoutTreeCrawler;
362
+    let anotherMockedUniqueIdProvider: UniqueIdProvider;
355 363
 
356 364
     beforeEach(() => {
357 365
       cb = jest.fn();
358
-      const mockParser = { parse: () => 'parsed' };
359
-      const mockCrawler = { crawl: (x: any) => x, processOptions: (x: any) => x };
366
+      mockedLayoutTreeParser = mock(LayoutTreeParser);
367
+      mockedLayoutTreeCrawler = mock(LayoutTreeCrawler);
360 368
       commandsObserver.register(cb);
361 369
       const mockedOptionsProcessor = mock(OptionsProcessor);
362
-      const optionsProcessor = instance(mockedOptionsProcessor);
370
+      anotherMockedUniqueIdProvider = mock(UniqueIdProvider);
371
+      when(anotherMockedUniqueIdProvider.generate(anything())).thenCall(
372
+        (prefix) => `${prefix}+UNIQUE_ID`
373
+      );
374
+
363 375
       uut = new Commands(
364 376
         mockedNativeCommandsSender,
365
-        mockParser as any,
366
-        mockCrawler as any,
377
+        instance(mockedLayoutTreeParser),
378
+        instance(mockedLayoutTreeCrawler),
367 379
         commandsObserver,
368
-        new UniqueIdProvider(),
369
-        optionsProcessor
380
+        instance(anotherMockedUniqueIdProvider),
381
+        instance(mockedOptionsProcessor)
370 382
       );
371 383
     });
372 384
 
@@ -377,29 +389,6 @@ describe('Commands', () => {
377 389
       return methods;
378 390
     }
379 391
 
380
-    // function getAllMethodsOfNativeCommandsSender() {
381
-    //   const nativeCommandsSenderFns = _.functions(mockedNativeCommandsSender);
382
-    //   expect(nativeCommandsSenderFns.length).toBeGreaterThan(1);
383
-    //   return nativeCommandsSenderFns;
384
-    // }
385
-
386
-    // it('always call last, when nativeCommand fails, dont notify listeners', () => {
387
-    //   // expect(getAllMethodsOfUut().sort()).toEqual(getAllMethodsOfNativeCommandsSender().sort());
388
-
389
-    //   // call all commands on uut, all should throw, no commandObservers called
390
-    //   _.forEach(getAllMethodsOfUut(), (m) => {
391
-    //     expect(() => uut[m]()).toThrow();
392
-    //     expect(cb).not.toHaveBeenCalled();
393
-    //   });
394
-    // });
395
-
396
-    // it('notify on all commands', () => {
397
-    //   _.forEach(getAllMethodsOfUut(), (m) => {
398
-    //     uut[m]({});
399
-    //   });
400
-    //   expect(cb).toHaveBeenCalledTimes(getAllMethodsOfUut().length);
401
-    // });
402
-
403 392
     describe('passes correct params', () => {
404 393
       const argsForMethodName: Record<string, any[]> = {
405 394
         setRoot: [{}],
@@ -418,20 +407,27 @@ describe('Commands', () => {
418 407
         getLaunchArgs: ['id']
419 408
       };
420 409
       const paramsForMethodName: Record<string, object> = {
421
-        setRoot: { commandId: 'setRoot+UNIQUE_ID', layout: { root: 'parsed', modals: [], overlays: [] } },
410
+        setRoot: {
411
+          commandId: 'setRoot+UNIQUE_ID',
412
+          layout: { root: null, modals: [], overlays: [] }
413
+        },
422 414
         setDefaultOptions: { options: {} },
423 415
         mergeOptions: { componentId: 'id', options: {} },
424
-        showModal: { commandId: 'showModal+UNIQUE_ID', layout: 'parsed' },
416
+        showModal: { commandId: 'showModal+UNIQUE_ID', layout: null },
425 417
         dismissModal: { commandId: 'dismissModal+UNIQUE_ID', componentId: 'id', mergeOptions: {} },
426 418
         dismissAllModals: { commandId: 'dismissAllModals+UNIQUE_ID', mergeOptions: {} },
427
-        push: { commandId: 'push+UNIQUE_ID', componentId: 'id', layout: 'parsed' },
419
+        push: { commandId: 'push+UNIQUE_ID', componentId: 'id', layout: null },
428 420
         pop: { commandId: 'pop+UNIQUE_ID', componentId: 'id', mergeOptions: {} },
429 421
         popTo: { commandId: 'popTo+UNIQUE_ID', componentId: 'id', mergeOptions: {} },
430 422
         popToRoot: { commandId: 'popToRoot+UNIQUE_ID', componentId: 'id', mergeOptions: {} },
431
-        setStackRoot: { commandId: 'setStackRoot+UNIQUE_ID', componentId: 'id', layout: ['parsed'] },
432
-        showOverlay: { commandId: 'showOverlay+UNIQUE_ID', layout: 'parsed' },
423
+        setStackRoot: {
424
+          commandId: 'setStackRoot+UNIQUE_ID',
425
+          componentId: 'id',
426
+          layout: [null]
427
+        },
428
+        showOverlay: { commandId: 'showOverlay+UNIQUE_ID', layout: null },
433 429
         dismissOverlay: { commandId: 'dismissOverlay+UNIQUE_ID', componentId: 'id' },
434
-        getLaunchArgs: { commandId: 'getLaunchArgs+UNIQUE_ID' },
430
+        getLaunchArgs: { commandId: 'getLaunchArgs+UNIQUE_ID' }
435 431
       };
436 432
       _.forEach(getAllMethodsOfUut(), (m) => {
437 433
         it(`for ${m}`, () => {

+ 88
- 157
lib/src/commands/LayoutTreeCrawler.test.ts Ver fichero

@@ -1,227 +1,158 @@
1 1
 import * as React from 'react';
2 2
 
3 3
 import { LayoutType } from './LayoutType';
4
-import { LayoutTreeCrawler, LayoutNode } from './LayoutTreeCrawler';
5
-import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock';
4
+import { LayoutTreeCrawler } from './LayoutTreeCrawler';
6 5
 import { Store } from '../components/Store';
7
-import { mock, instance } from 'ts-mockito';
6
+import { mock, instance, verify, deepEqual, when } from 'ts-mockito';
8 7
 import { OptionsProcessor } from './OptionsProcessor';
8
+import { Options } from '../interfaces/Options';
9 9
 
10 10
 describe('LayoutTreeCrawler', () => {
11 11
   let uut: LayoutTreeCrawler;
12
-  let store: Store;
12
+  let mockedStore: Store;
13
+  let mockedOptionsProcessor: OptionsProcessor;
13 14
 
14 15
   beforeEach(() => {
15
-    store = new Store();
16
-    const mockedOptionsProcessor = mock(OptionsProcessor);
17
-    const optionsProcessor = instance(mockedOptionsProcessor);
18
-    uut = new LayoutTreeCrawler(new UniqueIdProvider(), store, optionsProcessor);
19
-  });
16
+    mockedStore = mock(Store);
17
+    mockedOptionsProcessor = mock(OptionsProcessor);
20 18
 
21
-  it('crawls a layout tree and adds unique id to each node', () => {
22
-    const node = { type: LayoutType.Stack, id: 'Stack+UNIQUE_ID', children: [{ id: 'BottomTabs+UNIQUE_ID', type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
23
-    uut.crawl(node);
24
-    expect(node.id).toEqual('Stack+UNIQUE_ID');
25
-    expect(node.children[0].id).toEqual('BottomTabs+UNIQUE_ID');
26
-  });
27
-
28
-  it('does not generate unique id when already provided', () => {
29
-    const node = { id: 'user defined id', type: LayoutType.Stack, data: {}, children: [] };
30
-    uut.crawl(node);
31
-    expect(node.id).toEqual('user defined id');
32
-  });
33
-
34
-  it('crawls a layout tree and ensures data exists', () => {
35
-    const node = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
36
-    uut.crawl(node);
37
-    expect(node.data).toEqual({});
38
-    expect(node.children[0].data).toEqual({});
39
-  });
40
-
41
-  it('crawls a layout tree and ensures children exists', () => {
42
-    const node = { type: LayoutType.Stack, children: [{ type: LayoutType.BottomTabs, data: {}, children: [] }], data: {} };
43
-    uut.crawl(node);
44
-    expect(node.children[0].children).toEqual([]);
19
+    uut = new LayoutTreeCrawler(instance(mockedStore), instance(mockedOptionsProcessor));
45 20
   });
46 21
 
47 22
   it('saves passProps into store for Component nodes', () => {
48 23
     const node = {
24
+      id: 'testId',
49 25
       type: LayoutType.BottomTabs,
50
-      children: [{ type: LayoutType.Component, data: { name: 'the name', passProps: { myProp: 123 } }, children: [] }],
26
+      children: [
27
+        {
28
+          id: 'testId',
29
+          type: LayoutType.Component,
30
+          data: { name: 'the name', passProps: { myProp: 123 } },
31
+          children: []
32
+        }
33
+      ],
51 34
       data: {}
52 35
     };
53
-    expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual({});
54 36
     uut.crawl(node);
55
-    expect(store.getPropsForId('Component+UNIQUE_ID')).toEqual({ myProp: 123 });
37
+    verify(mockedStore.setPropsForId('testId', deepEqual({ myProp: 123 }))).called();
56 38
   });
57 39
 
58 40
   it('Components: injects options from original component class static property', () => {
59
-    const theStyle = {};
60
-    const MyComponent = class CoolComponent extends React.Component {
61
-      static get options() {
62
-        return theStyle;
63
-      }
41
+    when(mockedStore.getComponentClassForName('theComponentName')).thenReturn(
42
+      () =>
43
+        class extends React.Component {
44
+          static options(): Options {
45
+            return { popGesture: true };
46
+          }
47
+        }
48
+    );
49
+    const node = {
50
+      id: 'testId',
51
+      type: LayoutType.Component,
52
+      data: { name: 'theComponentName', options: {} },
53
+      children: []
64 54
     };
65
-
66
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
67
-    store.setComponentClassForName('theComponentName', () => MyComponent);
68 55
     uut.crawl(node);
69
-    expect(node.data.options).toEqual(theStyle);
56
+    expect(node.data.options).toEqual({ popGesture: true });
70 57
   });
71 58
 
72 59
   it('Components: crawl does not cache options', () => {
73
-    const optionsWithTitle = (title?: string) => {
74
-      return {
75
-        topBar: {
76
-          title: {
77
-            text: title
60
+    when(mockedStore.getComponentClassForName('theComponentName')).thenReturn(
61
+      () =>
62
+        class extends React.Component {
63
+          static options(props: { title?: string }) {
64
+            return { topBar: { title: { text: props.title } } };
78 65
           }
79 66
         }
80
-      };
81
-    };
82
-
83
-    const MyComponent = class CoolComponent extends React.Component {
84
-      static options(props: {title: string}) {
85
-        return {
86
-          topBar: {
87
-            title: {
88
-              text: props.title
89
-            }
90
-          }
91
-        };
92
-      }
93
-    };
94
-
95
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {}, passProps: { title: 'title' } }, children: [] };
96
-    store.setComponentClassForName('theComponentName', () => MyComponent);
97
-    uut.crawl(node);
98
-    expect(node.data.options).toEqual(optionsWithTitle('title'));
99
-
100
-    const node2 = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
101
-    uut.crawl(node2);
102
-    expect(node2.data.options).toEqual(optionsWithTitle(undefined));
103
-  });
104
-
105
-  it('Components: passes passProps to the static options function to be used by the user', () => {
106
-    const MyComponent = class CoolComponent extends React.Component {
107
-      static options(passProps: {bar: {baz: {value: string}}}) {
108
-        return { foo: passProps.bar.baz.value };
109
-      }
67
+    );
68
+    const node = {
69
+      id: 'testId',
70
+      type: LayoutType.Component,
71
+      data: { name: 'theComponentName', options: {}, passProps: { title: 'title' } },
72
+      children: []
110 73
     };
111
-
112
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', passProps: { bar: { baz: { value: 'hello' } } }, options: {} }, children: [] };
113
-    store.setComponentClassForName('theComponentName', () => MyComponent);
114 74
     uut.crawl(node);
115
-    expect(node.data.options).toEqual({ foo: 'hello' });
116
-  });
75
+    expect(node.data.options).toEqual({ topBar: { title: { text: 'title' } } });
117 76
 
118
-  it('Components: passProps in the static options is optional', () => {
119
-    const MyComponent = class CoolComponent extends React.Component {
120
-      static options(passProps: string) {
121
-        return { foo: passProps };
122
-      }
77
+    const node2 = {
78
+      id: 'testId',
79
+      type: LayoutType.Component,
80
+      data: { name: 'theComponentName', options: {} },
81
+      children: []
123 82
     };
124
-
125
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
126
-    store.setComponentClassForName('theComponentName', () => MyComponent);
127
-    uut.crawl(node);
128
-    expect(node.data.options).toEqual({ foo: {} });
83
+    uut.crawl(node2);
84
+    expect(node2.data.options).toEqual({ topBar: { title: {} } });
129 85
   });
130 86
 
131 87
   it('Components: merges options from component class static property with passed options, favoring passed options', () => {
132
-    const theStyle = {
133
-      bazz: 123,
134
-      inner: {
135
-        foo: 'bar'
136
-      },
137
-      opt: 'exists only in static'
138
-    };
139
-    const MyComponent = class CoolComponent extends React.Component {
140
-      static get options() {
141
-        return theStyle;
142
-      }
143
-    };
88
+    when(mockedStore.getComponentClassForName('theComponentName')).thenReturn(
89
+      () =>
90
+        class extends React.Component {
91
+          static options() {
92
+            return {
93
+              bazz: 123,
94
+              inner: { foo: 'this gets overriden' },
95
+              opt: 'exists only in static'
96
+            };
97
+          }
98
+        }
99
+    );
144 100
 
145
-    const passedOptions = {
146
-      aaa: 'exists only in passed',
147
-      bazz: 789,
148
-      inner: {
149
-        foo: 'this is overriden'
150
-      }
101
+    const node = {
102
+      id: 'testId',
103
+      type: LayoutType.Component,
104
+      data: {
105
+        name: 'theComponentName',
106
+        options: {
107
+          aaa: 'exists only in passed',
108
+          bazz: 789,
109
+          inner: { foo: 'this should override same keys' }
110
+        }
111
+      },
112
+      children: []
151 113
     };
152 114
 
153
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: passedOptions }, children: [] };
154
-    store.setComponentClassForName('theComponentName', () => MyComponent);
155
-
156 115
     uut.crawl(node);
157 116
 
158 117
     expect(node.data.options).toEqual({
159 118
       aaa: 'exists only in passed',
160 119
       bazz: 789,
161
-      inner: {
162
-        foo: 'this is overriden'
163
-      },
120
+      inner: { foo: 'this should override same keys' },
164 121
       opt: 'exists only in static'
165 122
     });
166 123
   });
167 124
 
168
-  it('Component: deepClones options', () => {
169
-    const theStyle = {};
170
-    const MyComponent = class CoolComponent extends React.Component {
171
-      static get options() {
172
-        return theStyle;
173
-      }
174
-    };
175
-
176
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
177
-    store.setComponentClassForName('theComponentName', () => MyComponent);
178
-    uut.crawl(node);
179
-    expect(node.data.options).not.toBe(theStyle);
180
-  });
181
-
182 125
   it('Components: must contain data name', () => {
183
-    const node = { type: LayoutType.Component, data: {}, children: [] };
126
+    const node = { type: LayoutType.Component, data: {}, children: [], id: 'testId' };
184 127
     expect(() => uut.crawl(node)).toThrowError('Missing component data.name');
185 128
   });
186 129
 
187 130
   it('Components: options default obj', () => {
188
-    const MyComponent = class extends React.Component { };
131
+    when(mockedStore.getComponentClassForName('theComponentName')).thenReturn(
132
+      () => class extends React.Component {}
133
+    );
189 134
 
190
-    const node = { type: LayoutType.Component, data: { name: 'theComponentName', options: {} }, children: [] };
191
-    store.setComponentClassForName('theComponentName', () => MyComponent);
135
+    const node = {
136
+      id: 'testId',
137
+      type: LayoutType.Component,
138
+      data: { name: 'theComponentName', options: {} },
139
+      children: []
140
+    };
192 141
     uut.crawl(node);
193 142
     expect(node.data.options).toEqual({});
194 143
   });
195 144
 
196 145
   it('Components: omits passProps after processing so they are not passed over the bridge', () => {
197 146
     const node = {
147
+      id: 'testId',
198 148
       type: LayoutType.Component,
199 149
       data: {
200 150
         name: 'compName',
201
-        passProps: {}
151
+        passProps: { someProp: 'here' }
202 152
       },
203 153
       children: []
204 154
     };
205 155
     uut.crawl(node);
206 156
     expect(node.data.passProps).toBeUndefined();
207 157
   });
208
-
209
-  describe('LayoutNode', () => {
210
-    it('convertable from same data structure', () => {
211
-      const x = {
212
-        id: 'theId',
213
-        type: LayoutType.Component,
214
-        data: {},
215
-        children: []
216
-      };
217
-
218
-      let got;
219
-      function expectingLayoutNode(param: LayoutNode) {
220
-        got = param;
221
-      }
222
-      expectingLayoutNode(x);
223
-
224
-      expect(got).toBe(x);
225
-    });
226
-  });
227 158
 });

+ 28
- 21
lib/src/commands/LayoutTreeCrawler.ts Ver fichero

@@ -1,7 +1,8 @@
1 1
 import * as _ from 'lodash';
2 2
 import { LayoutType } from './LayoutType';
3 3
 import { OptionsProcessor } from './OptionsProcessor';
4
-import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
4
+import { Store } from '../components/Store';
5
+import { Options } from '../interfaces/Options';
5 6
 
6 7
 export interface Data {
7 8
   name?: string;
@@ -9,49 +10,55 @@ export interface Data {
9 10
   passProps?: any;
10 11
 }
11 12
 export interface LayoutNode {
12
-  id?: string;
13
+  id: string;
13 14
   type: LayoutType;
14 15
   data: Data;
15 16
   children: LayoutNode[];
16 17
 }
17 18
 
19
+type ComponentWithOptions = React.ComponentType<any> & { options(passProps: any): Options };
20
+
18 21
 export class LayoutTreeCrawler {
19
-  constructor(
20
-    private readonly uniqueIdProvider: UniqueIdProvider,
21
-    public readonly store: any,
22
-    private readonly optionsProcessor: OptionsProcessor
23
-  ) {
22
+  constructor(public readonly store: Store, private readonly optionsProcessor: OptionsProcessor) {
24 23
     this.crawl = this.crawl.bind(this);
25 24
   }
26 25
 
27 26
   crawl(node: LayoutNode): void {
28
-    node.id = node.id || this.uniqueIdProvider.generate(node.type);
29 27
     if (node.type === LayoutType.Component) {
30
-      this._handleComponent(node);
28
+      this.handleComponent(node);
31 29
     }
32 30
     this.optionsProcessor.processOptions(node.data.options);
33
-    _.forEach(node.children, this.crawl);
31
+    node.children.forEach(this.crawl);
34 32
   }
35 33
 
36
-  _handleComponent(node) {
37
-    this._assertComponentDataName(node);
38
-    this._savePropsToStore(node);
39
-    this._applyStaticOptions(node);
34
+  private handleComponent(node: LayoutNode) {
35
+    this.assertComponentDataName(node);
36
+    this.savePropsToStore(node);
37
+    this.applyStaticOptions(node);
40 38
     node.data.passProps = undefined;
41 39
   }
42 40
 
43
-  _savePropsToStore(node) {
41
+  private savePropsToStore(node: LayoutNode) {
44 42
     this.store.setPropsForId(node.id, node.data.passProps);
45 43
   }
46 44
 
47
-  _applyStaticOptions(node) {
48
-    const clazz = this.store.getComponentClassForName(node.data.name) ? this.store.getComponentClassForName(node.data.name)() : {};
49
-    const staticOptions = _.isFunction(clazz.options) ? clazz.options(node.data.passProps || {}) : (_.cloneDeep(clazz.options) || {});
50
-    const passedOptions = node.data.options || {};
51
-    node.data.options = _.merge({}, staticOptions, passedOptions);
45
+  private isComponentWithOptions(component: any): component is ComponentWithOptions {
46
+    return (component as ComponentWithOptions).options !== undefined;
47
+  }
48
+
49
+  private staticOptionsIfPossible(node: LayoutNode) {
50
+    const foundReactGenerator = this.store.getComponentClassForName(node.data.name!);
51
+    const reactComponent = foundReactGenerator ? foundReactGenerator() : undefined;
52
+    return reactComponent && this.isComponentWithOptions(reactComponent)
53
+      ? reactComponent.options(node.data.passProps || {})
54
+      : {};
55
+  }
56
+
57
+  private applyStaticOptions(node: LayoutNode) {
58
+    node.data.options = _.merge({}, this.staticOptionsIfPossible(node), node.data.options);
52 59
   }
53 60
 
54
-  _assertComponentDataName(component) {
61
+  private assertComponentDataName(component: LayoutNode) {
55 62
     if (!component.data.name) {
56 63
       throw new Error('Missing component data.name');
57 64
     }

+ 39
- 29
lib/src/commands/LayoutTreeParser.test.ts Ver fichero

@@ -1,14 +1,19 @@
1
-import * as  _ from 'lodash';
1
+import * as _ from 'lodash';
2 2
 import { LayoutTreeParser } from './LayoutTreeParser';
3 3
 import { LayoutType } from './LayoutType';
4 4
 import { Layout } from '../interfaces/Layout';
5 5
 import { OptionsSplitView } from '../interfaces/Options';
6
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
7
+import { mock, instance, when, anything } from 'ts-mockito';
6 8
 
7 9
 describe('LayoutTreeParser', () => {
8 10
   let uut: LayoutTreeParser;
11
+  let mockedUniqueIdProvider: UniqueIdProvider;
9 12
 
10 13
   beforeEach(() => {
11
-    uut = new LayoutTreeParser();
14
+    mockedUniqueIdProvider = mock(UniqueIdProvider);
15
+    when(mockedUniqueIdProvider.generate(anything())).thenReturn('myUniqueId');
16
+    uut = new LayoutTreeParser(instance(mockedUniqueIdProvider));
12 17
   });
13 18
 
14 19
   describe('parses into { type, data, children }', () => {
@@ -18,16 +23,26 @@ describe('LayoutTreeParser', () => {
18 23
 
19 24
     it('single component', () => {
20 25
       expect(uut.parse(LayoutExamples.singleComponent)).toEqual({
26
+        id: 'myUniqueId',
21 27
         type: LayoutType.Component,
22
-        data: { name: 'MyReactComponent', options: LayoutExamples.options, passProps: LayoutExamples.passProps },
28
+        data: {
29
+          name: 'MyReactComponent',
30
+          options: LayoutExamples.options,
31
+          passProps: LayoutExamples.passProps
32
+        },
23 33
         children: []
24 34
       });
25 35
     });
26 36
 
27 37
     it('external component', () => {
28 38
       expect(uut.parse(LayoutExamples.externalComponent)).toEqual({
39
+        id: 'myUniqueId',
29 40
         type: LayoutType.ExternalComponent,
30
-        data: { name: 'MyReactComponent', options: LayoutExamples.options, passProps: LayoutExamples.passProps },
41
+        data: {
42
+          name: 'MyReactComponent',
43
+          options: LayoutExamples.options,
44
+          passProps: LayoutExamples.passProps
45
+        },
31 46
         children: []
32 47
       });
33 48
     });
@@ -40,6 +55,7 @@ describe('LayoutTreeParser', () => {
40 55
         }
41 56
       });
42 57
       expect(result).toEqual({
58
+        id: 'myUniqueId',
43 59
         type: LayoutType.Component,
44 60
         data: { name: 'MyScreen', passProps: LayoutExamples.passProps },
45 61
         children: []
@@ -49,17 +65,20 @@ describe('LayoutTreeParser', () => {
49 65
 
50 66
     it('stack of components with top bar', () => {
51 67
       expect(uut.parse(LayoutExamples.stackWithTopBar)).toEqual({
68
+        id: 'myUniqueId',
52 69
         type: LayoutType.Stack,
53 70
         data: {
54 71
           options: LayoutExamples.options
55 72
         },
56 73
         children: [
57 74
           {
75
+            id: 'myUniqueId',
58 76
             type: LayoutType.Component,
59 77
             data: { name: 'MyReactComponent1' },
60 78
             children: []
61 79
           },
62 80
           {
81
+            id: 'myUniqueId',
63 82
             type: LayoutType.Component,
64 83
             data: { name: 'MyReactComponent2', options: LayoutExamples.options },
65 84
             children: []
@@ -71,7 +90,7 @@ describe('LayoutTreeParser', () => {
71 90
     it('bottom tabs', () => {
72 91
       const result = uut.parse(LayoutExamples.bottomTabs);
73 92
       expect(_.keys(result)).toEqual(['id', 'type', 'data', 'children']);
74
-      expect(result.id).toEqual(undefined);
93
+      expect(result.id).toEqual('myUniqueId');
75 94
       expect(result.type).toEqual(LayoutType.BottomTabs);
76 95
       expect(result.data).toEqual({});
77 96
       expect(result.children.length).toEqual(3);
@@ -83,7 +102,7 @@ describe('LayoutTreeParser', () => {
83 102
     it('side menus', () => {
84 103
       const result = uut.parse(LayoutExamples.sideMenu);
85 104
       expect(_.keys(result)).toEqual(['id', 'type', 'data', 'children']);
86
-      expect(result.id).toEqual(undefined);
105
+      expect(result.id).toEqual('myUniqueId');
87 106
       expect(result.type).toEqual(LayoutType.SideMenuRoot);
88 107
       expect(result.data).toEqual({});
89 108
       expect(result.children.length).toEqual(3);
@@ -101,7 +120,7 @@ describe('LayoutTreeParser', () => {
101 120
     it('top tabs', () => {
102 121
       const result = uut.parse(LayoutExamples.topTabs);
103 122
       expect(_.keys(result)).toEqual(['id', 'type', 'data', 'children']);
104
-      expect(result.id).toEqual(undefined);
123
+      expect(result.id).toEqual('myUniqueId');
105 124
       expect(result.type).toEqual(LayoutType.TopTabs);
106 125
       expect(result.data).toEqual({ options: LayoutExamples.options });
107 126
       expect(result.children.length).toEqual(5);
@@ -136,7 +155,9 @@ describe('LayoutTreeParser', () => {
136 155
     expect(uut.parse({ stack: { options } }).data.options).toBe(options);
137 156
     expect(uut.parse({ bottomTabs: { options } }).data.options).toBe(options);
138 157
     expect(uut.parse({ topTabs: { options } }).data.options).toBe(options);
139
-    expect(uut.parse({ sideMenu: { options, center: { component: {name: 'lool'} } } }).data.options).toBe(options);
158
+    expect(
159
+      uut.parse({ sideMenu: { options, center: { component: { name: 'lool' } } } }).data.options
160
+    ).toBe(options);
140 161
     expect(uut.parse(LayoutExamples.splitView).data.options).toBe(optionsSplitView);
141 162
   });
142 163
 
@@ -146,7 +167,9 @@ describe('LayoutTreeParser', () => {
146 167
     expect(uut.parse({ stack: { id: 'stackId' } }).id).toEqual('stackId');
147 168
     expect(uut.parse({ stack: { children: [{ component }] } }).children[0].id).toEqual('compId');
148 169
     expect(uut.parse({ bottomTabs: { id: 'myId' } }).id).toEqual('myId');
149
-    expect(uut.parse({ bottomTabs: { children: [{ component }] } }).children[0].id).toEqual('compId');
170
+    expect(uut.parse({ bottomTabs: { children: [{ component }] } }).children[0].id).toEqual(
171
+      'compId'
172
+    );
150 173
     expect(uut.parse({ topTabs: { id: 'myId' } }).id).toEqual('myId');
151 174
     expect(uut.parse({ topTabs: { children: [{ component }] } }).children[0].id).toEqual('compId');
152 175
     expect(uut.parse({ sideMenu: { id: 'myId', center: { component } } }).id).toEqual('myId');
@@ -174,7 +197,7 @@ const optionsSplitView: OptionsSplitView = {
174 197
   displayMode: 'auto',
175 198
   primaryEdge: 'leading',
176 199
   minWidth: 150,
177
-  maxWidth: 300,
200
+  maxWidth: 300
178 201
 };
179 202
 
180 203
 const singleComponent = {
@@ -236,13 +259,7 @@ const sideMenu = {
236 259
 
237 260
 const topTabs = {
238 261
   topTabs: {
239
-    children: [
240
-      singleComponent,
241
-      singleComponent,
242
-      singleComponent,
243
-      singleComponent,
244
-      stackWithTopBar
245
-    ],
262
+    children: [singleComponent, singleComponent, singleComponent, singleComponent, stackWithTopBar],
246 263
     options
247 264
   }
248 265
 };
@@ -257,12 +274,7 @@ const complexLayout: Layout = {
257 274
           stackWithTopBar,
258 275
           {
259 276
             stack: {
260
-              children: [
261
-                singleComponent,
262
-                singleComponent,
263
-                singleComponent,
264
-                singleComponent,
265
-              ]
277
+              children: [singleComponent, singleComponent, singleComponent, singleComponent]
266 278
             }
267 279
           }
268 280
         ]
@@ -275,15 +287,13 @@ const splitView: Layout = {
275 287
   splitView: {
276 288
     master: {
277 289
       stack: {
278
-        children: [
279
-          singleComponent,
280
-        ],
290
+        children: [singleComponent],
281 291
         options
282
-      },
292
+      }
283 293
     },
284 294
     detail: stackWithTopBar,
285
-    options: optionsSplitView,
286
-  },
295
+    options: optionsSplitView
296
+  }
287 297
 };
288 298
 
289 299
 const LayoutExamples = {

+ 18
- 11
lib/src/commands/LayoutTreeParser.ts Ver fichero

@@ -1,4 +1,3 @@
1
-import * as _ from 'lodash';
2 1
 import { LayoutType } from './LayoutType';
3 2
 import { LayoutNode } from './LayoutTreeCrawler';
4 3
 import {
@@ -11,9 +10,10 @@ import {
11 10
   LayoutSplitView,
12 11
   ExternalComponent
13 12
 } from '../interfaces/Layout';
13
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
14 14
 
15 15
 export class LayoutTreeParser {
16
-  constructor() {
16
+  constructor(private uniqueIdProvider: UniqueIdProvider) {
17 17
     this.parse = this.parse.bind(this);
18 18
   }
19 19
 
@@ -33,12 +33,12 @@ export class LayoutTreeParser {
33 33
     } else if (api.splitView) {
34 34
       return this.splitView(api.splitView);
35 35
     }
36
-    throw new Error(`unknown LayoutType "${_.keys(api)}"`);
36
+    throw new Error(`unknown LayoutType "${Object.keys(api)}"`);
37 37
   }
38 38
 
39 39
   private topTabs(api: TopTabs): LayoutNode {
40 40
     return {
41
-      id: api.id,
41
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.TopTabs),
42 42
       type: LayoutType.TopTabs,
43 43
       data: { options: api.options },
44 44
       children: api.children ? api.children.map(this.parse) : []
@@ -47,7 +47,7 @@ export class LayoutTreeParser {
47 47
 
48 48
   private sideMenu(api: LayoutSideMenu): LayoutNode {
49 49
     return {
50
-      id: api.id,
50
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.SideMenuRoot),
51 51
       type: LayoutType.SideMenuRoot,
52 52
       data: { options: api.options },
53 53
       children: this.sideMenuChildren(api)
@@ -57,15 +57,22 @@ export class LayoutTreeParser {
57 57
   private sideMenuChildren(api: LayoutSideMenu): LayoutNode[] {
58 58
     const children: LayoutNode[] = [];
59 59
     if (api.left) {
60
-      children.push({ type: LayoutType.SideMenuLeft, data: {}, children: [this.parse(api.left)] });
60
+      children.push({
61
+        id: this.uniqueIdProvider.generate(LayoutType.SideMenuLeft),
62
+        type: LayoutType.SideMenuLeft,
63
+        data: {},
64
+        children: [this.parse(api.left)]
65
+      });
61 66
     }
62 67
     children.push({
68
+      id: this.uniqueIdProvider.generate(LayoutType.SideMenuCenter),
63 69
       type: LayoutType.SideMenuCenter,
64 70
       data: {},
65 71
       children: [this.parse(api.center)]
66 72
     });
67 73
     if (api.right) {
68 74
       children.push({
75
+        id: this.uniqueIdProvider.generate(LayoutType.SideMenuRight),
69 76
         type: LayoutType.SideMenuRight,
70 77
         data: {},
71 78
         children: [this.parse(api.right)]
@@ -76,7 +83,7 @@ export class LayoutTreeParser {
76 83
 
77 84
   private bottomTabs(api: LayoutBottomTabs): LayoutNode {
78 85
     return {
79
-      id: api.id,
86
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.BottomTabs),
80 87
       type: LayoutType.BottomTabs,
81 88
       data: { options: api.options },
82 89
       children: api.children ? api.children.map(this.parse) : []
@@ -85,7 +92,7 @@ export class LayoutTreeParser {
85 92
 
86 93
   private stack(api: LayoutStack): LayoutNode {
87 94
     return {
88
-      id: api.id,
95
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.Stack),
89 96
       type: LayoutType.Stack,
90 97
       data: { options: api.options },
91 98
       children: api.children ? api.children.map(this.parse) : []
@@ -94,7 +101,7 @@ export class LayoutTreeParser {
94 101
 
95 102
   private component(api: LayoutComponent): LayoutNode {
96 103
     return {
97
-      id: api.id,
104
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.Component),
98 105
       type: LayoutType.Component,
99 106
       data: { name: api.name.toString(), options: api.options, passProps: api.passProps },
100 107
       children: []
@@ -103,7 +110,7 @@ export class LayoutTreeParser {
103 110
 
104 111
   private externalComponent(api: ExternalComponent): LayoutNode {
105 112
     return {
106
-      id: api.id,
113
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.ExternalComponent),
107 114
       type: LayoutType.ExternalComponent,
108 115
       data: { name: api.name.toString(), options: api.options, passProps: api.passProps },
109 116
       children: []
@@ -115,7 +122,7 @@ export class LayoutTreeParser {
115 122
     const detail = api.detail ? this.parse(api.detail) : undefined;
116 123
 
117 124
     return {
118
-      id: api.id,
125
+      id: api.id || this.uniqueIdProvider.generate(LayoutType.SplitView),
119 126
       type: LayoutType.SplitView,
120 127
       data: { options: api.options },
121 128
       children: master && detail ? [master, detail] : []

+ 0
- 1
lib/src/commands/OptionsProcessor.test.ts Ver fichero

@@ -1,7 +1,6 @@
1 1
 import { OptionsProcessor } from './OptionsProcessor';
2 2
 import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
3 3
 import { Store } from '../components/Store';
4
-import * as _ from 'lodash';
5 4
 import { Options, OptionsModalPresentationStyle } from '../interfaces/Options';
6 5
 import { mock, when, anyString, instance, anyNumber, verify } from 'ts-mockito';
7 6
 import { ColorService } from '../adapters/ColorService';

+ 2
- 2
lib/src/components/ComponentWrapper.test.tsx Ver fichero

@@ -159,7 +159,7 @@ describe('ComponentWrapper', () => {
159 159
 
160 160
   describe(`register with redux store`, () => {
161 161
     class MyReduxComp extends React.Component<any> {
162
-      static get options() {
162
+      static options() {
163 163
         return { foo: 123 };
164 164
       }
165 165
       render() {
@@ -185,7 +185,7 @@ describe('ComponentWrapper', () => {
185 185
       const NavigationComponent = uut.wrap(componentName, () => ConnectedComp, store, componentEventsObserver, undefined, ReduxProvider, reduxStore);
186 186
       const tree = renderer.create(<NavigationComponent componentId={'theCompId'} />);
187 187
       expect(tree.toJSON()!.children).toEqual(['it just works']);
188
-      expect((NavigationComponent as any).options).toEqual({ foo: 123 });
188
+      expect((NavigationComponent as any).options()).toEqual({ foo: 123 });
189 189
     });
190 190
   });
191 191
 

+ 8
- 8
lib/src/components/Store.ts Ver fichero

@@ -1,8 +1,8 @@
1
-import * as React from 'react';
2 1
 import * as _ from 'lodash';
2
+import { ComponentProvider } from 'react-native';
3 3
 
4 4
 export class Store {
5
-  private componentsByName: Record<string, () => React.ComponentClass<any, any>> = {};
5
+  private componentsByName: Record<string, ComponentProvider> = {};
6 6
   private propsById: Record<string, any> = {};
7 7
 
8 8
   setPropsForId(componentId: string, props: any) {
@@ -13,15 +13,15 @@ export class Store {
13 13
     return _.get(this.propsById, componentId, {});
14 14
   }
15 15
 
16
-  setComponentClassForName(componentName: string | number, ComponentClass: () => React.ComponentClass<any, any>) {
17
-    _.set(this.componentsByName, componentName.toString(), ComponentClass);
16
+  cleanId(componentId: string) {
17
+    _.unset(this.propsById, componentId);
18 18
   }
19 19
 
20
-  getComponentClassForName(componentName: string | number) {
21
-    return _.get(this.componentsByName, componentName.toString());
20
+  setComponentClassForName(componentName: string | number, ComponentClass: ComponentProvider) {
21
+    _.set(this.componentsByName, componentName.toString(), ComponentClass);
22 22
   }
23 23
 
24
-  cleanId(componentId: string) {
25
-    _.unset(this.propsById, componentId);
24
+  getComponentClassForName(componentName: string | number): ComponentProvider | undefined {
25
+    return _.get(this.componentsByName, componentName.toString());
26 26
   }
27 27
 }

+ 2
- 2
lib/src/events/CommandsObserver.ts Ver fichero

@@ -1,7 +1,7 @@
1 1
 import { EventSubscription } from '../interfaces/EventSubscription';
2 2
 import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
3 3
 
4
-export type CommandsListener = (name: string, params: any) => void;
4
+export type CommandsListener = (name: string, params: Record<string, any>) => void;
5 5
 
6 6
 export class CommandsObserver {
7 7
   private listeners: Record<string, CommandsListener> = {};
@@ -15,7 +15,7 @@ export class CommandsObserver {
15 15
     };
16 16
   }
17 17
 
18
-  public notify(commandName: string, params: any): void {
18
+  public notify(commandName: string, params: Record<string, any>): void {
19 19
     Object.values(this.listeners).forEach((listener) => listener(commandName, params));
20 20
   }
21 21
 }

+ 4
- 2
lib/src/events/ComponentEventsObserver.ts Ver fichero

@@ -12,8 +12,10 @@ import {
12 12
 } from '../interfaces/ComponentEvents';
13 13
 import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
14 14
 
15
+type ReactComponentWithIndexing = React.Component<any> & Record<string, any>;
16
+
15 17
 export class ComponentEventsObserver {
16
-  private readonly listeners = {};
18
+  private listeners: Record<string, Record<string, ReactComponentWithIndexing>> = {};
17 19
   private alreadyRegistered = false;
18 20
 
19 21
   constructor(private readonly nativeEventsReceiver: NativeEventsReceiver) {
@@ -87,7 +89,7 @@ export class ComponentEventsObserver {
87 89
 
88 90
   private triggerOnAllListenersByComponentId(event: ComponentEvent, method: string) {
89 91
     _.forEach(this.listeners[event.componentId], (component) => {
90
-      if (_.isObject(component) && _.isFunction(component[method])) {
92
+      if (component[method]) {
91 93
         component[method](event);
92 94
       }
93 95
     });

+ 1
- 1
lib/src/events/EventsRegistry.test.tsx Ver fichero

@@ -1,7 +1,7 @@
1 1
 import { EventsRegistry } from './EventsRegistry';
2 2
 import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver.mock';
3 3
 import { CommandsObserver } from './CommandsObserver';
4
-import { UniqueIdProvider } from '../adapters/UniqueIdProvider.mock';
4
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
5 5
 
6 6
 describe('EventsRegistry', () => {
7 7
   let uut: EventsRegistry;

+ 1
- 1
playground/src/screens/BackHandlerModalScreen.js Ver fichero

@@ -4,7 +4,7 @@ const { Component } = require('react');
4 4
 const { View, Text, Button, BackHandler } = require('react-native');
5 5
 
6 6
 class BackHandlerModalScreen extends Component {
7
-  static get options() {
7
+  static options() {
8 8
     return {
9 9
       topBar: {
10 10
         title: {

+ 1
- 1
playground/src/screens/BackHandlerScreen.js Ver fichero

@@ -4,7 +4,7 @@ const { Navigation } = require('react-native-navigation');
4 4
 const { View, Text, Button, BackHandler } = require('react-native');
5 5
 
6 6
 class BackHandlerScreen extends Component {
7
-  static get options() {
7
+  static options() {
8 8
     return {
9 9
       topBar: {
10 10
         title: {

+ 1
- 1
playground/src/screens/CustomDialog.js Ver fichero

@@ -7,7 +7,7 @@ const { Navigation } = require('react-native-navigation');
7 7
 const testIDs = require('../testIDs');
8 8
 
9 9
 class CustomDialog extends PureComponent {
10
-  static get options() {
10
+  static options() {
11 11
     return {
12 12
       statusBarBackgroundColor: 'green'
13 13
     };

+ 1
- 1
playground/src/screens/CustomTransitionDestination.js Ver fichero

@@ -11,7 +11,7 @@ class CustomTransitionDestination extends Component {
11 11
     this.push = this.push.bind(this);
12 12
   }
13 13
 
14
-  static get options() {
14
+  static options() {
15 15
     return {
16 16
       topBar: {
17 17
         title: {

+ 1
- 1
playground/src/screens/CustomTransitionOrigin.js Ver fichero

@@ -8,7 +8,7 @@ class CustomTransitionOrigin extends Component {
8 8
     super(props);
9 9
     this.onClickNavigationIcon = this.onClickNavigationIcon.bind(this);
10 10
   }
11
-  static get options() {
11
+  static options() {
12 12
     return {
13 13
       topBar: {
14 14
         title: {

+ 1
- 1
playground/src/screens/KeyboardScreen.js Ver fichero

@@ -22,7 +22,7 @@ const LOREM_IPSUM = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, se
22 22
                     'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
23 23
 
24 24
 class KeyboardScreen extends Component {
25
-  static get options() {
25
+  static options() {
26 26
     return {
27 27
       bottomTabs: {
28 28
         drawBehind: true,

+ 1
- 1
playground/src/screens/ModalScreen.js Ver fichero

@@ -9,7 +9,7 @@ const { Navigation } = require('react-native-navigation');
9 9
 const testIDs = require('../testIDs');
10 10
 
11 11
 class ModalScreen extends Component {
12
-  static get options() {
12
+  static options() {
13 13
     return {
14 14
       statusBar: {
15 15
         visible: false,

+ 1
- 1
playground/src/screens/OptionsScreen.js Ver fichero

@@ -20,7 +20,7 @@ class OptionsScreen extends Component {
20 20
     Navigation.events().bindComponent(this);
21 21
   }
22 22
 
23
-  static get options() {
23
+  static options() {
24 24
     return {
25 25
       statusBar: {
26 26
         style: 'dark',

+ 1
- 1
playground/src/screens/PushedScreen.js Ver fichero

@@ -7,7 +7,7 @@ const Button = require('./Button');
7 7
 const testIDs = require('../testIDs');
8 8
 
9 9
 class PushedScreen extends Component {
10
-  static get options() {
10
+  static options() {
11 11
     return {
12 12
       _statusBar: {
13 13
         visible: false,

+ 1
- 1
playground/src/screens/ScrollViewScreen.js Ver fichero

@@ -9,7 +9,7 @@ const testIDs = require('../testIDs');
9 9
 const FAB = 'fab';
10 10
 
11 11
 class ScrollViewScreen extends Component {
12
-  static get options() {
12
+  static options() {
13 13
     return {
14 14
       topBar: {
15 15
         title: {

+ 1
- 1
playground/src/screens/SearchScreen.js Ver fichero

@@ -20,7 +20,7 @@ for(let i = 0; i < 200; i++) {
20 20
 }
21 21
 
22 22
 class SearchControllerScreen extends Component {
23
-  static get options() {
23
+  static options() {
24 24
     return {
25 25
       topBar: {
26 26
         title: {

+ 1
- 1
playground/src/screens/TextScreen.js Ver fichero

@@ -7,7 +7,7 @@ const { Navigation } = require('react-native-navigation');
7 7
 const testIDs = require('../testIDs');
8 8
 const Bounds = require('../components/Bounds');
9 9
 class TextScreen extends Component {
10
-  static get options() {
10
+  static options() {
11 11
     return {
12 12
       bottomTabs: {
13 13
         drawBehind: true,

+ 1
- 1
playground/src/screens/TopTabOptionsScreen.js Ver fichero

@@ -5,7 +5,7 @@ const { View, Text, Button } = require('react-native');
5 5
 const { Navigation } = require('react-native-navigation');
6 6
 
7 7
 class TopTabOptionsScreen extends PureComponent {
8
-  static get options() {
8
+  static options() {
9 9
     return {
10 10
       topBar: {
11 11
         title: {

+ 1
- 1
playground/src/screens/TopTabScreen.js Ver fichero

@@ -5,7 +5,7 @@ const { View, Text } = require('react-native');
5 5
 const FAB = 'fab';
6 6
 
7 7
 class TopTabScreen extends PureComponent {
8
-  static get options() {
8
+  static options() {
9 9
     return {
10 10
       topBar: {
11 11
         title: {

+ 1
- 1
playground/src/screens/WelcomeScreen.js Ver fichero

@@ -8,7 +8,7 @@ const Button = require('./Button');
8 8
 const { Navigation } = require('react-native-navigation');
9 9
 
10 10
 class WelcomeScreen extends Component {
11
-  static get options() {
11
+  static options() {
12 12
     return {
13 13
       _statusBar: {
14 14
         backgroundColor: 'transparent',

+ 1
- 1
playground/src/screens/complexlayouts/BottomTabSideMenuScreen.js Ver fichero

@@ -5,7 +5,7 @@ const { Navigation } = require('react-native-navigation');
5 5
 const testIDs = require('../../testIDs');
6 6
 
7 7
 class BottomTabSideMenuScreen extends Component {
8
-  static get options() {
8
+  static options() {
9 9
     return {
10 10
       topBar: {
11 11
         title: {

+ 1
- 6
tsconfig-strict.json Ver fichero

@@ -4,20 +4,15 @@
4 4
     "allowSyntheticDefaultImports": false,
5 5
     "allowUnreachableCode": false,
6 6
     "allowUnusedLabels": false,
7
-    "alwaysStrict": true,
8 7
     "diagnostics": true,
9 8
     "forceConsistentCasingInFileNames": true,
10 9
     "importHelpers": true,
11 10
     "noEmitOnError": true,
12 11
     "noFallthroughCasesInSwitch": true,
13 12
     "noImplicitReturns": true,
14
-    "noImplicitThis": true,
15 13
     "noUnusedLocals": true,
16 14
     "noUnusedParameters": true,
17 15
     "pretty": true,
18
-    "strictFunctionTypes": true,
19
-    "strictNullChecks": true,
20
-    "strict": true,
21
-    "noImplicitAny": false
16
+    "strict": true
22 17
   }
23 18
 }