├── .all-contributorsrc ├── .babelrc ├── .github └── CODEOWNERS ├── .gitignore ├── .idea └── vcs.xml ├── .nvmrc ├── .travis.yml ├── .vscode └── settings.json ├── LICENCE.MD ├── README.MD ├── SUMMARY.MD ├── _config.yml ├── assets ├── bad-configuration-naming-example.png ├── codepush.png └── good-configuration-naming-example.png ├── backend ├── django │ ├── create-model-and-api.mo.md │ ├── create-user-model.mo.md │ ├── deploy-to-aws.mo.md │ └── getting-started.mo.md ├── graphql-js │ ├── assets │ │ ├── authorization.png │ │ └── presentation_layer.png │ └── getting-started-with-apollo-server-dataloader-knex.mo.md └── node-js │ ├── add-multiple-environments-configuration-on-loopback.s.md │ └── handle-errors-and-exceptions-in-javascript.s.md ├── code-quality ├── components-state-testing.mo.md ├── mock-with-jest.mo.md ├── pull-request-template.s.md ├── pull_request_template.md └── test-files-indentation.s.md ├── contributing ├── assets │ └── PR.png ├── contributing.mo.md ├── mo.s.md └── standard.s.md ├── dangerfile.js ├── editors ├── boost-up-my-code-editor.s.md └── vscode │ └── setup-vscode.mo.md ├── export_to_m33_blog.sh ├── flowtype └── flowtype.s.md ├── git └── merge-or-rebase-a-branch.md ├── intro.tpl ├── javascript └── promisify-a-callback.mo.md ├── jsconfig.json ├── ops └── docker │ ├── deploy-with-https.mo.md │ └── docker-nginx-companion-error.png ├── package.json ├── performance ├── backend │ ├── cache-routes-using-varnish.mo.md │ ├── how-to-investigate-performance.mo.md │ ├── minimize-number-sql-queries.mo.md │ ├── output-sql-alchemy-orm-query.mo.md │ ├── python-investigation-tools.mo.md │ └── serve-images-as-static-files.mo.md ├── front │ ├── how-to-investigate-performance.mo.md │ ├── react-native-maps-performance.s.md │ ├── react-native-performance.s.md │ ├── simulate-network-iphone.mo.md │ └── table-and-chart-with-good-performance.mo.md └── performance-decision-flow.s.md ├── project-standards ├── project-success │ ├── index.md │ └── production.s.md ├── taking-over-project │ ├── index.md │ └── migrate-to-new-ios-certificates.mo.md └── technical-agility │ ├── code-vocabulary-identical-business-vocabulary.s.md │ ├── index.md │ ├── react-native-test.s.md │ ├── under-15-minutes-project-installation.s.md │ └── up-to-date-dependencies.s.md ├── react-native ├── animations │ └── react-native-animations.s.md ├── architecture │ ├── default-stack.s.md │ ├── file-naming.s.md │ └── project-architecture.s.md ├── debugging │ ├── analyse-bug.jpg │ ├── analyse-bug.mo.md │ ├── debug-javascript-ios-device.mo.md │ ├── debug-javascript.mo.md │ ├── debug-native-android.mo.md │ ├── debug-native-ios.mo.md │ ├── debug-network-calls.mo.md │ ├── debug-two-ios-apps-simultaneously.mo.md │ ├── debug-webviews.mo.md │ ├── get-ios-logs.mo.md │ └── handle-gradle-dependencies-clash.mo.md ├── features │ ├── asyncstorage.mo.md │ ├── clean-logout.s.md │ ├── deep-linking.md │ ├── icomoon.mo.md │ ├── lock-device-orientation.mo.md │ ├── offline-mobx.mo.md │ └── offline-redux.mo.md ├── firebase │ ├── assets │ │ ├── example-tickets.png │ │ ├── firebase-debug-view.png │ │ └── xcode-firebase-debug-setup.png │ └── debug-events.mo.md ├── package-dependencies │ └── handle-dependencies-with-yarn-override.mo.md ├── react-navigation │ └── unmount-compoenent-on-page-change.mo.md ├── react │ ├── assets │ │ ├── withUncle.png │ │ └── withoutUncle.png │ ├── binding-functions-in-react-component.s.md │ ├── enable-overflow-android.mo.md │ └── get-element-size-or-position-with-onLayout.mo.md ├── setup │ ├── add-cocoapods.mo.md │ ├── add-native-module.mo.md │ ├── assets │ │ ├── firebase_android.png │ │ ├── ios_steps.png │ │ └── stripe_basic_payment.png │ ├── deploy-project-to-production.md │ ├── deploy-script.mo.md │ ├── deploy-to-production-android.mo.md │ ├── deploy.sh │ ├── overriding-existing-app.s.md │ ├── patch-react-native-android.mo.md │ ├── remove-unnecessary-android-permissions.mo.md │ ├── setup-and-deploy-new-project-to-staging-with-hockeyapp.mo.md │ ├── setup-and-deploy-new-project-to-staging.mo.md │ ├── setup-code-push.mo.md │ ├── setup-facebook-login.mo.md │ ├── setup-stripe-dev-standard.mo.md │ └── setup_firebase_multiple_envs.mo.md ├── tests │ └── setup-detox-jest.mo.md ├── update │ └── upgrade-react-native.mo.md └── use_http_links_in_react_native.mo.md ├── react ├── component.s.md ├── lifecycle │ └── trigger-action-on-props-update.mo.md └── redux │ ├── custom-redux-form-field.mo.md │ └── pass-props-to-container.mo.md ├── scrum └── timebox.s.md ├── security ├── 2FA.mo.md └── import_certificates_match.mo.md ├── successful-sprint └── coding │ ├── bad_example_car1.png │ ├── bad_example_car2.png │ └── plan-your-ticket-to-improve-efficency.s.md ├── templates └── mo.md └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @louiszawadzki @tychota 2 | backend/django/* @yleflour @xavierlefevre @Samox 3 | backend/graphql-js/* @yleflour @tpucci 4 | contributing/* @yleflour 5 | editor/vscode/* @yleflour 6 | flowtype/* @yleflour 7 | git/* @tychota 8 | project-standards/technical-agility/* 9 | security/* @minishlink 10 | dangerfile.js @yleflour 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 7.7.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | before_script: 4 | - yarn danger 5 | notifications: 6 | email: false 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.printWidth": 120, 3 | "[html]": { 4 | "editor.formatOnSave": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /assets/bad-configuration-naming-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/assets/bad-configuration-naming-example.png -------------------------------------------------------------------------------- /assets/codepush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/assets/codepush.png -------------------------------------------------------------------------------- /assets/good-configuration-naming-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/assets/good-configuration-naming-example.png -------------------------------------------------------------------------------- /backend/django/create-user-model.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Create Complex User Models in Django in 1/2 hour (~30 min) 2 | 3 | ## Owner: [Sammy Teillet](https://github.com/samox) 4 | 5 | ## Context 6 | 7 | - You have a Django project installed. 8 | - You need to handle different types of users (for instance an Actor, a Producer, a Director) that will all inherit from the user, but will have different properties. 9 | - You want to handle them from the admin. 10 | 11 | ## Prerequisites (~45min) 12 | 13 | - Have the project installed, [see this](https://github.com/bamlab/dev-standards/blob/master/backend/django/getting-started.mo.md) 14 | 15 | ## Situation 16 | 17 | - You have a basic user model with a name. 18 | - You want to create another type of user, a Mayor that has a City (the city is a foreign key to a City model). 19 | - You do not want to override the classic user model. 20 | - You want to create a Mayor from the admin 21 | - You want the newly created Mayor to be part of the Staff (Staff User can access the admin, but they are not SuperUser) 22 | 23 | ## Steps 24 | 25 | ### Add the Mayor model (5 min) 26 | 27 | - In the models.py file, where the User is defined, create a new class that inherits the User 28 | 29 | ```python 30 | # You should have the User in the same file, or from app.models import User 31 | from django.db import models 32 | from django.contrib.auth.models import User 33 | 34 | 35 | class Mayor(User): 36 | city = models.ForeignKey('locations.city') 37 | 38 | class Meta: 39 | verbose_name = "Mayor" 40 | ``` 41 | 42 | - Create the migration file: `./manage.py makemigrations` or `docker-compose run django_container_name ./manage.py makemigrations` 43 | - Run the migration: `./manage.py migrate` or `docker-compose run django_container_name ./manage.py migrate` 44 | 45 | > **Check 1:** Run the show migration command to see the new one ``./manage.py showmigrations`` 46 | 47 | ### Create a Mayor from the admin (5 min) 48 | 49 | - In the admin.py, where the User model is added `admin.register(User)` 50 | - Add the following: 51 | 52 | ```python 53 | from .models import Mayor 54 | from django.contrib.auth.admin import UserAdmin 55 | 56 | @admin.register(Mayor) 57 | class MyMayorAdmin(UserAdmin): 58 | pass 59 | ``` 60 | 61 | > **Check 1:** You now see the Mayor in the admin. But all the forms (form to add a Mayor, form to change/update a Mayor) are the same as for the regular User. 62 | 63 | ### Override the admin form to add a Mayor (10 min) 64 | 65 | - In the admin.py when you declared your MyMayorAdmin class, you can choose the fields you want to display in the form by overriding the `add_fieldsets` property. You can readme more about `add_fieldset` [here:](https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#a-full-example) 66 | - You need to know the fields you want to display. `username`, `password1` and `password2` are default fields of the "user admin add form" 67 | - In our case: 68 | 69 | ```python 70 | from .models import Mayor 71 | from django.contrib.auth.admin import UserAdmin 72 | 73 | class MyMayorAdmin(AuthUserAdmin): 74 | add_fieldsets = ( 75 | ('User Profile', {'fields': ('username', 'city', 'password1', 'password2')}), 76 | ) 77 | ``` 78 | 79 | > **Check 1:** You now see the city field in the "add new mayor" form. 80 | 81 | ### Give admin property to mayor user (10 min) 82 | 83 | - You can manually set a user to be part of the staff, you can also override the save method of the AddForm so it changes the property is_staff to True 84 | - First create a form that inherits the AddUserForm and override the save method (see [overriding custom model method](https://docs.djangoproject.com/en/1.11/topics/db/models/#overriding-predefined-model-methods)): 85 | 86 | ```python 87 | from django.contrib.auth.forms import UserCreationForm 88 | 89 | class MyMayorCreationForm(UserCreationForm): 90 | 91 | class Meta(UserCreationForm.Meta): 92 | model = Mayor 93 | 94 | def save(self, commit=True): 95 | user = super().save(commit=False) 96 | user.is_staff = True 97 | if commit: 98 | user.save() 99 | return user 100 | ``` 101 | 102 | - Then use this form in the MyMayorAdmin class: 103 | 104 | ```python 105 | @admin.register(Mayor) 106 | class MyMayorAdmin(AuthUserAdmin): 107 | add_form = MyMayorCreationForm 108 | ``` 109 | 110 | > **Check 1**: When I create a mayor, I see that it is staff user in the detail view of the mayor. 111 | -------------------------------------------------------------------------------- /backend/graphql-js/assets/authorization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/backend/graphql-js/assets/authorization.png -------------------------------------------------------------------------------- /backend/graphql-js/assets/presentation_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/backend/graphql-js/assets/presentation_layer.png -------------------------------------------------------------------------------- /backend/node-js/add-multiple-environments-configuration-on-loopback.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Add multiple environments configuration on loopback 2 | 3 | ## Owner: Maxime Sraïki 4 | 5 | # Why 6 | 7 | Whenever you are developing a loopback application, you'll need to have multiple environments in order to control the impact of your development team on the other environments: dev, staging, prod... 8 | 9 | ## Checks 10 | 11 | * You need to have a root configuration file with all the trans environment datas 12 | * Each environment specific configuration file has to be named with the same standard 13 | * Each environment specific configuration file has to have only the informations of its own environment 14 | 15 | ## Examples 16 | 17 | ## Bad Examples 18 | 19 | ### Example 1: Wrong configuration files naming, they don't respect the same pattern 20 | 21 | ![Wrong configuration files naming](/assets/bad-configuration-naming-example.png) 22 | 23 | ### Example 2: Multiple environments variables in one config file 24 | 25 | ```json 26 | { 27 | "key1": "value1", 28 | "key2": "value2", 29 | "key3": { 30 | "staging": "value3-staging", 31 | "prod": "value3-prod", 32 | } 33 | } 34 | ``` 35 | 36 | ## Good Examples 37 | 38 | ### Example 1: Good configuration files naming 39 | 40 | ![Good configuration files naming](/assets/good-configuration-naming-example.png) 41 | 42 | ### Example 2: Only own environments variables in one config file 43 | 44 | ```json 45 | { 46 | "key1": "value1", 47 | "key2": "value2", 48 | "key3": "value3" 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /backend/node-js/handle-errors-and-exceptions-in-javascript.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Handle Errors and Exceptions in Javascript 2 | 3 | ## Owner: Maxime Sraïki 4 | 5 | ## Why 6 | 7 | * On scaling projects you can easily be overwhelmed by a pretty large code source. When this occurs, if you don't have a proper way to handle errors/exceptions, you could easily start raising a lot of bug or regression and spend a lot of time trying to fix it. 8 | 9 | ## Definitions 10 | 11 | * First let's make the distinction between Erros and Exceptions: 12 | - Errors are javascript objects that are thrown when something goes wrong in the code during runtime. It will usually have: 13 | - a `message` key containing a human readable error message 14 | - informations about where the error was raised like `fileName`, `lineNumber`, or even the whole `stack` 15 | - Exceptions are custom Errors objects created by developers to implement business logic. 16 | 17 | ## Checks 18 | 19 | * Every Exceptions should be caught in a .catch statement and treated. 20 | * Every caught Error should be rethrown (Even if it makes your application crash, you have to see it QUICKLY) 21 | * Every .catch statement should take the error as argument 22 | * [OPTIONAL] If you have a logging system, in every .catch statement, you should log a message with the context and the whole error object 23 | 24 | 25 | ## Examples 26 | 27 | ### Bad Examples: 28 | #### Example 1: No catch statement 29 | 30 | ```js 31 | myFunction().then(res => { 32 | //Do something with res 33 | }) 34 | ``` 35 | 36 | Here if myFunction throws an error, it will remain unhandled and can make my server crash without prior notice ! 37 | 38 | #### Example 2: Error no rethrown 39 | 40 | ```js 41 | myFunction().then(res => { 42 | //Do something with res 43 | }).catch(console.log) 44 | ``` 45 | 46 | Here if myFunction throws an error, it will be caught in the .catch statement and logged to the console logger. The problem are: 47 | - The statement 48 | ```js 49 | .catch(console.log) 50 | ``` 51 | is equivalent to 52 | ```js 53 | .catch(err => console.log(err)) 54 | ``` 55 | which is 56 | ```js 57 | .catch(err => {return console.log(err)}) 58 | ``` 59 | therefore it will go in your next .then and act as if everything was normal with an undefined response... 60 | - You make the error disappear and it will not be caught in your next .catch 61 | 62 | #### Example 3: Error not even considered 63 | 64 | ```js 65 | myFunction().then(res => { 66 | //Do something with res 67 | }).catch(() => { 68 | //Whatever 69 | }) 70 | ``` 71 | 72 | If you don't even use the error that has been caught, you are sure you don't react correctly to it. 73 | 74 | ### Good Examples: 75 | #### Example 1: Error 76 | 77 | ```js 78 | myFunction(argument).then(res => { 79 | //Do something with res 80 | }).catch(error => { 81 | logger.log('The following Error occured when executing myFunction with argument', { error, argument }) 82 | return Promise.reject(err) 83 | }) 84 | ``` 85 | 86 | #### Example 2: Exception 87 | 88 | ```js 89 | myFunction(argument).then(res => { 90 | //Do something with res 91 | }).catch(error => { 92 | if (error.msg === EXPECTED_MESSAGE) { 93 | // Do something with error 94 | } 95 | logger.log('The following Error occured when executing myFunction with argument', { error, argument }) 96 | return Promise.reject(err) 97 | }) 98 | ``` 99 | -------------------------------------------------------------------------------- /code-quality/components-state-testing.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Test your React and React Native components' state with Jest (without Enzyme) *(~35 min)* 2 | 3 | ## Owner: [Tycho Tatitscheff](https://github.com/tychota) 4 | 5 | ## Prerequisites *(~5 min)* 6 | - [ ] Have [Jest](https://facebook.github.io/jest/) installed 7 | - [ ] On React, follow the Jest [documentation](https://facebook.github.io/jest/docs/en/tutorial-react.html#content) 8 | - [ ] On React Native, it's already done (if needed, jest [doc](https://facebook.github.io/jest/docs/en/tutorial-react-native.html#content)) 9 | 10 | ## Steps *(~30 min)* 11 | - Create a simple test: 12 | ```javascript 13 | describe('ComponentToTest', () => { 14 | it('should test a simple true === true', () => { 15 | expect(true).toBe(true); 16 | }); 17 | }); 18 | ``` 19 | - Import the necessary packages and your component: 20 | ```javascript 21 | import React from 'react'; 22 | import renderer from 'react-test-renderer'; 23 | 24 | import ComponentToTest from './ComponentToTest.component'; 25 | ``` 26 | - Add the first test of your component, and remove the true === true 27 | - Render your component with react-test-renderer, it will transform your component in a JavaScript object instance, which you will be able to test, accessing its state, triggering its methods: 28 | ```javascript 29 | describe('ComponentToTest', () => { 30 | const props = {}; 31 | 32 | const component = renderer.create( 33 | 36 | ); 37 | //... 38 | }); 39 | ``` 40 | - Use this instance in your `it` test, the property `getInstance()` allows you to access all the properties of the component class. Now you can test your initial state: 41 | ```javascript 42 | describe('ComponentToTest', () => { 43 | // ... 44 | it('should init the state', () => { 45 | expect(component.getInstance().state).toEqual({ 46 | fakeStatus: 'init', 47 | }); 48 | }); 49 | }); 50 | ``` 51 | - Add new tests, faking a user action and the impact it has on the component state: 52 | ```javascript 53 | describe('ComponentToTest', () => { 54 | // ... 55 | it('should set the component fakeStatus to "inProgress"', () => { 56 | component.getInstance().onButtonPress(); 57 | expect(component.getInstance().state).toEqual({ 58 | fakeStatus: 'inProgress', 59 | }); 60 | }); 61 | // ... 62 | }); 63 | ``` 64 | -------------------------------------------------------------------------------- /code-quality/mock-with-jest.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] How to use mocks with jest (~2minutes) 2 | 3 | ## Owner: [Pierre-Louis Le Portz](https://github.com/pleportz) 4 | 5 | ## Why 6 | 7 | Most teams at BAM use jest for testing their react components. **One of the most frequent andon is about mocks.** 8 | 9 | Typical use case: 10 | *"My component's snapshot test was passing but then I imported a module and now the test is broken :'("* 11 | 12 | ## Prerequisites 13 | 14 | Have jest installed ;) 15 | 16 | ## Steps 17 | 18 | In this MO you will learn to: 19 | - mock some method on an imported module 20 | - mock an imported component 21 | - mock a class that is used for both rendering a component AND using static methods 22 | 23 | ## Example 1: mock some method on an imported module 24 | 25 | **File to be tested:** 26 | 27 | ```javascript 28 | import React, { PureComponent } from 'react'; 29 | import { View } from 'react-native'; 30 | import Permissions from 'react-native-permissions'; 31 | 32 | const checkPushNotifsPermission = () => { 33 | return Permissions.check('notification').then(status => { 34 | // do something depending on status 35 | }); 36 | }; 37 | 38 | export default class Home extends PureComponent { 39 | // do something with checkPushNotifsPermission ... 40 | render() { 41 | return ( 42 | 43 | // render something... 44 | 45 | ); 46 | } 47 | } 48 | ``` 49 | 50 | **Test file with snapshot test:** 51 | 52 | ```javascript 53 | 54 | import React from 'react'; 55 | import renderer from 'react-test-renderer'; 56 | import { Home } from './Home'; 57 | 58 | jest.mock('react-native-permissions', () => ({ 59 | check: _ => Promise.resolve(true), 60 | })); 61 | 62 | describe('', () => { 63 | it('renders correctly', () => { 64 | const props = { 65 | // props to give to the Home component 66 | }; 67 | const tree = renderer.create(); 68 | expect(tree.toJSON()).toMatchSnapshot(); 69 | }); 70 | }); 71 | ``` 72 | 73 | **Even better** 74 | 75 | In the example above we are mocking a native module (`react-native-permissions`). Since you always need to mock a native module, **you should centralize the mock definition in order to avoid redefining it in numerous test files**. Here is how to do it: 76 | 77 | ```javascript 78 | //project_root/__mocks__/react-native-permissions.js 79 | 80 | jest.mock('react-native-permissions', () => ({ 81 | check: _ => Promise.resolve(true), 82 | })); 83 | ``` 84 | 85 | ## Example 2: Mock one of your own components 86 | 87 | *In the example below, we chose to mock the Votes component when we added a container with graphql logic around the original Votes component* 88 | 89 | **File to be tested:** 90 | 91 | ```javascript 92 | import React, { PureComponent } from 'react'; 93 | import { View } from 'react-native'; 94 | import { Votes } from '../../components/Votes/Votes.container' 95 | 96 | export default class Recipe extends PureComponent { 97 | render() { 98 | return ( 99 | 100 | // render things and then use: 101 | 102 | 103 | ); 104 | } 105 | } 106 | ``` 107 | 108 | **Test file with snapshot test:** 109 | 110 | ```javascript 111 | import React from 'react'; 112 | import renderer from 'react-test-renderer'; 113 | import Recipe from './Recipe'; 114 | 115 | jest.mock('../../components/Votes/Votes.container', () => props => ); 116 | 117 | // Here we use WITHOUT a capital letter 118 | 119 | describe('', () => { 120 | it('renders correctly', () => { 121 | const props = { 122 | // props to give to the Recipe component 123 | }; 124 | const tree = renderer.create(); 125 | expect(tree.toJSON()).toMatchSnapshot(); 126 | }); 127 | }); 128 | ``` 129 | 130 | ## Example 3: Mock a class that is used for both rendering a component AND using static methods 131 | 132 | In the example below we use the `react-rte/lib/RichTextEditor` module to build our own state-controlled markdown input component. 133 | 134 | 135 | **File to be tested:** 136 | 137 | ```javascript 138 | import React, { Component } from 'react'; 139 | import RichTextEditor from 'react-rte/lib/RichTextEditor'; 140 | 141 | export default class MarkdownInput extends Component{ 142 | 143 | // use RichTextEditor.createValueFromString() and RichTextEditor.createEmptyValue() in some lifecycle methods 144 | 145 | render() { 146 | return ; 149 | } 150 | } 151 | ``` 152 | 153 | **Centralized mock (see Example 1, section "Even better"):** 154 | 155 | ```javascript 156 | jest.mock('path_from_root_to_node_modules/node_modules/react-rte/lib/RichTextEditor', () => { 157 | const RichTextEditor = props => ; 158 | RichTextEditor.createValueFromString = string => ({ content: string }); 159 | RichTextEditor.createEmptyValue = () => ({ content: '' }); 160 | return RichTextEditor; 161 | }); 162 | ``` 163 | -------------------------------------------------------------------------------- /code-quality/pull-request-template.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Pull Request Template 2 | 3 | ## Owner: [Xavier Lefèvre](https://github.com/xavierlefevre) 4 | 5 | ## Description 6 | - A Pull Request template is a markdown document shown every time a pull request is made by a developer. You can put any content in it, it usually serves the developer and its reviewer as a reminder of what a good PR should be. 7 | 8 | ## Impact 9 | - This tool is mostly used in order to give contexts to reviewers about what and how the developer developed its feature. It's also extremely useful to remind to the developer and the reviewer some key development standards they should not forget or overlook such as unit testing your app or testing your app against different types of machines/devices. 10 | 11 | ## Checks 12 | - [ ] Include, adapt and version the [attached pull request template](/code-quality/pull_request_template.md) to the base of your project 13 | 14 | ## Bad Examples 15 | *TBD* 16 | 17 | ## Good Examples 18 | *TBD* 19 | -------------------------------------------------------------------------------- /code-quality/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | Every time a developer will make a pull request and a team-mate will review the work, the below points need to be checked! 4 | 5 | ## Reviewee 6 | 7 | - [ ] PR Title = Ticket Title 8 | - [ ] Ticket link: 9 | - [ ] Visuals: 10 | 11 | App Screenshot | UX Mock-up 12 | ------ | ------- 13 | 14 | - [ ] Flow typed 15 | - [ ] Logic unit tested 16 | - [ ] Peer review 17 | - [ ] Show live feature to reviewer 18 | - Definition of done: 19 | - [ ] Android: Sony Experia SP 20 | - [ ] iOS: iPhone 6S iOS 10 21 | 22 | ## Reviewer 23 | 24 | - [ ] Read and understand the ticket ("What" + "How") 25 | - [ ] No missing logic test 26 | - [ ] No missing flow typing 27 | - [ ] No missing refactoring 28 | -------------------------------------------------------------------------------- /code-quality/test-files-indentation.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Test files indentation 2 | 3 | ## Owner: [Pierre-Louis Le Portz](https://github.com/pleportz) 4 | 5 | ## Why 6 | 7 | Standardizing the indentation of a test file saves the developer 30 seconds per new function to test. 8 | 9 | ## Checks 10 | 11 | There are 2 cases: 12 | 13 | ### Case 1: The tested file exports at least 1 named function 14 | 15 | Use the following indentation even if only one function is tested: 16 | - 1 `describe` per file 17 | - 1 `describe` per function 18 | - 1 `it` for each case 19 | 20 | ### Case 2: The tested file exports only 1 function or element as `default` 21 | 22 | Use the following indentation: 23 | - 1 `describe` with the name of the file 24 | - 1 `it` for each functionality 25 | 26 | ## Good examples 27 | 28 | ### Case 1: Generic example 29 | 30 | ``` javascript 31 | describe('Name of the tested file', () => { 32 | describe('function1', () => { 33 | it('does something', () => { 34 | //... 35 | }); 36 | it('does some other thing', () => { 37 | //... 38 | }); 39 | }); 40 | 41 | describe('function2', () => { 42 | it('does something', () => { 43 | //... 44 | }); 45 | it('does some other thing', () => { 46 | //... 47 | }); 48 | }); 49 | }); 50 | ``` 51 | 52 | ### Case 1: A saga test with redux-saga-test-plan 53 | 54 | ```javascript 55 | import { testSaga } from 'redux-saga-test-plan'; 56 | import { buyRoamingPackage, fetchRoamingBundlesSaga } from './sagas'; 57 | import { selectedRoamingPackageSelector } from '../../TopUps/Bundle/selectors'; 58 | import { setLoading } from '../../LoadingStatus/actions'; 59 | 60 | describe('sagas', () => { 61 | describe('buyRoamingPackage', () => { 62 | it('initializes the product and buy it', () => { 63 | const roamingPackage = { ouid: '4' }; 64 | testSaga(buyRoamingPackage, { type: 'BUY_ROAMING_PACKAGE' }) 65 | .next() 66 | .select(selectedRoamingPackageSelector) 67 | // ... 68 | .isDone(); 69 | }); 70 | }); 71 | 72 | describe('fetchRoamingBundlesSaga', () => { 73 | it('fetches the roaming bundles and store them', () => { 74 | testSaga(fetchRoamingBundlesSaga, { 75 | type: 'FETCH_ROAMING_BUNDLES', 76 | }) 77 | .next() 78 | .put(setLoading('roamingOffer', true)) 79 | // ... 80 | .isDone(); 81 | }); 82 | }); 83 | }); 84 | ``` 85 | 86 | ### Case 2: A react-native component 87 | 88 | ```javascript 89 | import React from 'react'; 90 | import IdentityRequirements from './IdentityRequirements'; 91 | import renderer from 'react-test-renderer'; 92 | 93 | describe('', () => { 94 | it('renders correctly', () => { 95 | const tree = renderer.create(); 96 | expect(tree.toJSON()).toMatchSnapshot(); 97 | }); 98 | }); 99 | ``` 100 | 101 | ## Bad examples // @TODO 102 | -------------------------------------------------------------------------------- /contributing/assets/PR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/contributing/assets/PR.png -------------------------------------------------------------------------------- /contributing/contributing.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Contributing 2 | 3 | ## Owner: [Yann Leflour](https://github.com/yleflour) 4 | 5 | ## Prerequisites: 6 | 7 | - You have cloned [the repository](https://github.com/bamlab/dev-standards) locally 8 | - You have a [markdown/text editor](/editors/vscode/setup-vscode.mo.md) 9 | 10 | ## Steps: 11 | 12 | ### 1. Writing *(Variable time)* 13 | 14 | - For a Method of Operation please read [the following standard](/contributing/mo.s.md) 15 | - For a Standard please read [the following standard](/contributing/standard.s.md) 16 | - Create a branch and a pull request 17 | 18 | > **Check:** Your article is visible in the [pull requests list](https://github.com/bamlab/dev-standards/pulls) 19 | 20 | ### 2. Review *(~3min)* 21 | 22 | - Assign 2 contributors for review 23 | - Add the *Review Required* label 24 | 25 | > **Check:** Your PR side bar should look like this 26 | > ![image](assets/PR.png) 27 | 28 | ### 2bis. Fixing *(Variable time)* 29 | 30 | When a reviewer has reviewed your article they may give you some feedback. 31 | 32 | - Correct what needs to be corrected 33 | - Answer the comments which don't need fixing 34 | - Add the *Review Required* label 35 | 36 | > **Check:** Every comment is either deprecated or answered on Github and the label is *Review Required* 37 | 38 | ### 3. Publishing *(~0min)* 39 | 40 | - Once all reviews are ok, the last reviewer will merge your pull request and delete your branch 41 | - Publishing your branch will be automatic 42 | 43 | > **Check:** Your article can be found on [the Gitbook website](https://bamtech.gitbooks.io/dev-standards/) 44 | 45 | > **Troubleshooting:** Contact [Tycho](https://github.com/tychota) 46 | -------------------------------------------------------------------------------- /contributing/mo.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Method of Operation 2 | 3 | ## Owner: Yann Leflour 4 | 5 | ## Checks: 6 | 7 | - File name should be kebab case and suffixed by `.mo` 8 | - Is referenced in the root *README.MD* and *SUMMARY.MD* 9 | - Should contain the following parts: 10 | - An owner with a link to their Github Profile 11 | - Title + time 12 | - A list of prerequisites 13 | - A list of steps 14 | - A *Name* and aproximate *time to complete* 15 | - A *how to* 16 | - A way to *check* that the step has been completed appropriately 17 | - Troubleshooting *(optional)* 18 | - Title should be prefixed by *[MO]* 19 | 20 | ## Bad Examples 21 | 22 | ### Example 1: [[MO] Debug Javascript on an iOS Device](/react-native/debugging/debug-javascript-ios-device.mo.md) 23 | 24 | - There is no owner 25 | - No time is mentioned 26 | - No checks are mentioned 27 | - Troubleshooting is not in its own part 28 | 29 | ## Good Examples 30 | 31 | ### Example 2: [Kick start a JS GraphQL 3 layers API with Apollo-server, Dataloader and Knex (~75min)](/backend/graphql-js/getting-started-with-apollo-server-dataloader-knex.mo.md) 32 | -------------------------------------------------------------------------------- /contributing/standard.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Writing a standard 2 | 3 | ## Owner: Yann Leflour 4 | 5 | # Why 6 | 7 | The why part is important so Bamers know what is the risk addressed by this standard. 8 | 9 | Thus they have more context and are able to onboard other dev by presenting the impact. 10 | 11 | ## Checks 12 | - Has an owner with a link to their Github Profile 13 | - Should contain all the the parts present in this document 14 | - Title 15 | - Checks 16 | - Bad example(s) 17 | - Based on real world use cases 18 | - Good example 19 | - Based on real world use case 20 | - Title should be prefixed by [Standard] 21 | - File name should be kebab case and suffixed by `.s` 22 | - Referenced in the root *README.MD* and *SUMMARY.MD* 23 | 24 | ## Examples 25 | 26 | ### Example 1: [[Standard] Project Architecture](/react-native/architecture/project-architecture.s.md) 27 | 28 | - There is no owner 29 | - There are no examples 30 | 31 | ### Example 2: 32 | 33 | This page is an example of what a good standard is 34 | -------------------------------------------------------------------------------- /dangerfile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const { includes, concat } = require("lodash"); 4 | const minimatch = require("minimatch"); 5 | 6 | const modifiedFiles = danger.git.modified_files; 7 | const newFiles = danger.git.created_files; 8 | const changedFiles = [...modifiedFiles, ...newFiles]; 9 | 10 | const moFiles = changedFiles.filter(p => includes(p, ".mo.md")); 11 | const standardFiles = changedFiles.filter(p => includes(p, ".s.md")); 12 | const readmeContent = fs.readFileSync("README.MD").toString(); 13 | const summaryContent = fs.readFileSync("SUMMARY.MD").toString(); 14 | 15 | for (let moFile of moFiles) { 16 | const fileContent = fs.readFileSync(moFile).toString(); 17 | const fileUrl = danger.github.utils.fileLinks([moFile]); 18 | 19 | if (!fileContent.match(/^# \[MO\] /)) fail(`**${fileUrl}**: Title doesn't contain \`[MO]\` tag at the beginning`); 20 | if (!fileContent.match(/## Owner: .+/)) fail(`**${fileUrl}**: MO doesn't have an *Owner*, could it be you?`); 21 | if (!fileContent.match(/## Prerequisites/)) 22 | warn(`**${fileUrl}**: MO doesn't contain a *Prerequisites* part are you sure your brain is all you need?`); 23 | if (!fileContent.match(/## Steps/)) 24 | fail(`**${fileUrl}**: MO doesn't have a *Steps* part, how could you call this an MO?`); 25 | if (!fileContent.match(/## Troubleshooting/)) warn(`**${fileUrl}**: Seems you do not need a *Troubleshoot* part`); 26 | if (!readmeContent.match(moFile)) warn(`**${fileUrl}**: Does not seem to be included in the root readme`); 27 | if (!summaryContent.match(moFile)) warn(`**${fileUrl}**: Does not seem to be included in the root summary`); 28 | } 29 | 30 | for (let standardFile of standardFiles) { 31 | const fileContent = fs.readFileSync(standardFile).toString(); 32 | const fileUrl = danger.github.utils.fileLinks([standardFile]); 33 | 34 | if (!fileContent.match(/^# \[Standard\] /)) 35 | fail(`**${fileUrl}**: Title doesn't contain \`[Standard]\` tag at the beginning`); 36 | if (!fileContent.match(/## Owner: .+/)) fail(`**${fileUrl}**: Standard doesn't have an *Owner*, could it be you?`); 37 | if (!fileContent.match(/## Checks/)) 38 | fail(`**${fileUrl}**: Standard doesn't have a *Checks* part, how could you call this an Standard?`); 39 | if (!fileContent.match(/Bad Examples?/i)) fail(`**${fileUrl}**: You failed to mention a *Bad Examples*`); 40 | if (!fileContent.match(/Good Examples?/i)) fail(`**${fileUrl}**: You failed to mention a *Good Examples* `); 41 | if (!readmeContent.match(standardFile)) warn(`**${fileUrl}**: Does not seem to be included in the root readme`); 42 | if (!summaryContent.match(standardFile)) warn(`**${fileUrl}**: Does not seem to be included in the root summary`); 43 | } 44 | 45 | if (moFiles.length === 0 && standardFiles.length === 0) { 46 | fail(`What have you modified? No \`*.s.md\` and no \`*.mo.md\` files`); 47 | markdown(` 48 | ## What have you modified? No \`*.s.md\` and no \`*.mo.md\` files 49 | 50 | - Your standard files should be \`*.s.md\` 51 | - Your method of operation should be \`*.mo.md\` 52 | 53 | The modified files are: 54 | ${changedFiles.map(file => `- \`${file}\`\n`)} 55 | `); 56 | } 57 | 58 | const codeowners = fs.readFileSync(".github/CODEOWNERS", "utf8").split("\n"); 59 | let mentions = []; 60 | codeowners.forEach(codeowner => { 61 | const pattern = codeowner.split(" ")[0]; 62 | const owners = codeowner 63 | .substring(pattern.length) 64 | .trim() 65 | .split(" "); 66 | 67 | const modifiedFileHasOwner = path => minimatch(path, pattern); 68 | const modifiesOwnedCode = danger.git.modified_files.filter(modifiedFileHasOwner).length > 0; 69 | 70 | if (modifiesOwnedCode) { 71 | mentions = mentions.concat(owners); 72 | } 73 | }); 74 | const isOwnedCodeModified = mentions.length > 0; 75 | if (isOwnedCodeModified) { 76 | const uniqueMentions = new Set(mentions); 77 | markdown(`## Automatic reviewers 78 | 79 | cc: ${[...uniqueMentions].join(", ")}`); 80 | } 81 | -------------------------------------------------------------------------------- /editors/boost-up-my-code-editor.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Boost up my code editor 2 | 3 | ## Owner: [Guillaume Diallo-Mulliez](https://github.com/guitoof) 4 | 5 | ## Why ? 6 | 7 | As a developer my code editor is my main working tool. I need to be able to configure it properly and use it efficiently. 8 | 9 | ## Checks 10 | 11 | My editor allows me to: 12 | 13 | ### 🐞 **Properly Debug**: so that I can quickly identify the responsibility of an unexpected behaviour 14 | 15 | ##### What ? 16 | 17 | - Setup break points 18 | - Inspect current context variables 19 | 20 | ##### How ? 21 | 22 | - for **VSCode**: [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) 23 | 24 | ### 🎨 **Automatically Format my Code**: so that I can follow my team syntactic standards and ease up code review and maintanability 25 | 26 | ##### What ? 27 | 28 | - Real time or On save formatting 29 | - Share configuration with team mates 30 | 31 | #### How ? 32 | 33 | - for **VSCode**: [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 34 | 35 | ### 🔍 **Quickly Spot Syntactic Errors**: so that I don't need to run my linter/type checker commands after each modification 36 | 37 | ##### What ? 38 | 39 | - Linter 40 | - Type checking (flow, typescript) 41 | 42 | #### How ? 43 | 44 | - for **VSCode**: 45 | - for **ESLint** : [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) 46 | - for **TSLint** : [TSLint](https://marketplace.visualstudio.com/items?itemName=eg2.tslint) 47 | - for **Flow** : [vscode-flow-ide](https://marketplace.visualstudio.com/items?itemName=gcazaciuc.vscode-flow-ide) 48 | 49 | ### ☁️ **Visualize my git diff**: so that I can see what I changed since the last time it worked 50 | 51 | ##### What ? 52 | 53 | - See the git diff 54 | - Revert changes on a file 55 | 56 | #### How ? 57 | 58 | - for **VSCode**: Build in _Source Control_ tab 59 | 60 | ### 🛠 **Create & use snippets**: so that I can create basic files in a flash 61 | 62 | ##### What ? 63 | 64 | - Create snippets for components, containers, tests, redux files ... 65 | - Share your snippets with your team to ensure consistency 66 | 67 | #### How ? 68 | 69 | - for **VSCode**: [Project Snippets](https://marketplace.visualstudio.com/items?itemName=rebornix.project-snippets) 70 | - Create your snippets quickly with [VS-snippeteer](https://pierpo.github.io/vs-snippeteer/) 71 | 72 | ### 🔮 **Use Autocompletion**: so that I can avoid typos and code faster 73 | 74 | ##### What ? 75 | 76 | - Variables & function names 77 | - Relative and absolute paths 78 | 79 | #### How ? 80 | 81 | - for **VSCode**: [Flow Language Support](https://marketplace.visualstudio.com/items?itemName=flowtype.flow-for-vscode), [vscode-flow-ide](https://marketplace.visualstudio.com/items?itemName=gcazaciuc.vscode-flow-ide) 82 | 83 | ### ⛵️ **Navigate throughout the code in a click**: so that I don't waste time fumbling through a complex tree view 84 | 85 | ##### What ? 86 | 87 | - Variables, Functions, Classes **definition** 88 | - Variables, Functions, Classes **documentation** 89 | - Variables, Functions, Classes **typing** 90 | - Show file location 91 | 92 | #### How ? 93 | 94 | - for **VSCode**: [Flow Language Support](https://marketplace.visualstudio.com/items?itemName=flowtype.flow-for-vscode), [vscode-flow-ide](https://marketplace.visualstudio.com/items?itemName=gcazaciuc.vscode-flow-ide), [breadcrumbs](https://code.visualstudio.com/updates/v1_26#_breadcrumbs) 95 | 96 | ### 📦 **Identify Outdated packages**: so that I can upgrade them rapidly after a new release 97 | 98 | ##### What ? 99 | 100 | - See the latest stable version of each package 101 | 102 | #### How ? 103 | 104 | - for **VSCode**: [Version Lens](https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens) 105 | 106 | ## Good Examples 107 | 108 | 🛠 Need help to setup your editor to comply with these standards ? 109 | 110 | - Setup VSCode : https://bamtech.gitbook.io/dev-standards/editors/visual-studio-code/mo-setup-vscode 111 | 112 | ## Bad Examples 113 | 114 | Your current editor 😜 115 | -------------------------------------------------------------------------------- /editors/vscode/setup-vscode.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup VSCode 2 | 3 | ## Owner: [Yann Leflour](https://github.com/yleflour) 4 | ## Steps 5 | 6 | ### Installation 7 | Download and Install [Vscode](https://code.visualstudio.com/) 8 | 9 | ### Base Setup 10 | 11 | #### 1. The `code` command 12 | 13 | - Hit `cmd+shift+p` 14 | - Select `Shell Command: Install command 'code' in PATH` 15 | - You are now able to launch `code ` in your terminal 16 | 17 | #### 2. Base plugins 18 | 19 | Open the plugins menu and install: 20 | 21 | - ESLint *(Dirk Baeumer)* 22 | - Flow Language Support *(flowtype)* 23 | - npm Intellisense *(Christian Kohler)* 24 | - Path Intellisense *(Christian Kohler)* 25 | - Prettier *(Esben Petersen)* 26 | - React Native Tools *(Visual Studio Mobile Tools)* 27 | - Auto Close Tag *(Jun Han)* 28 | - Git Lens *(Eric Amodio)* 29 | - Jest *(Orta)* 30 | - Color Highlight *(Sergii Naumov)* 31 | - Dotenv *(mikestead)* 32 | - TSLint *(egamma)* 33 | - [Version Lens *(pflannery)*](https://marketplace.visualstudio.com/items?itemName=pflannery.vscode-versionlens) 34 | 35 | #### 3. Base preferences 36 | 37 | - Hit `cmd+shift+p` 38 | - Select `Preferences: Open User Settings` 39 | - Add the following: 40 | ```json 41 | "editor.tabSize": 2, 42 | "editor.formatOnSave": true, 43 | "files.insertFinalNewline": true, 44 | "flow.useNPMPackagedFlow": true, 45 | "workbench.iconTheme": "vs-seti", 46 | "files.associations": { 47 | "Fastfile": "ruby" 48 | } 49 | ``` 50 | 51 | #### 4. Usefull Project preferences 52 | 53 | - Hit `cmd+shift+p` 54 | - Select `Preferences: Open Workspace Settings` 55 | - Add the following: 56 | - For a react project with Flow `"javascript.validate.enable": false` 57 | -------------------------------------------------------------------------------- /export_to_m33_blog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | # ✔ rename file adding date and add to export folder 5 | # ✔ add header 6 | # ✔ convert path to category, prefixed with BAM, using last directory as category 7 | # ✔ convert slug to category name 8 | # - FOR EACH 9 | 10 | DEST_DIR="export" 11 | 12 | if [ ! -d "$DEST_DIR" ]; then 13 | mkdir $DEST_DIR 14 | fi 15 | 16 | # DEBUG: 17 | # echo $FILES 18 | 19 | for FILE in $(find . -mindepth 2 -iname '*.md') 20 | do 21 | TEMPLATE="intro.tpl" 22 | DATE=$(git log --follow --date=short --format=%ad -- "$FILE" | tail -1) 23 | NEW_NAME="$DATE-$(basename $FILE)" 24 | DEST="$DEST_DIR"/"$NEW_NAME" 25 | 26 | cp "$TEMPLATE" "$DEST" 27 | cat $FILE >> $DEST 28 | 29 | TITLE=$(head -1 $FILE | sed -r 's/#\s?//g') 30 | CATEGORY=$(dirname $FILE | grep -o '[^/]*$' | sed 's/^\(.\)/\U\1/' | sed 's/-/ /g') 31 | sed -i -- "s/%CATEGORY%/${CATEGORY//\//\\/}/g" $DEST 32 | sed -i -- "s/%TITLE%/${TITLE//\//\\/}/g" $DEST 33 | done -------------------------------------------------------------------------------- /flowtype/flowtype.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Flow Type 2 | 3 | ## Owner: [Tycho Tatitscheff](https://github.com/tychota) 4 | 5 | ## Why 6 | 7 | Flowtype is awesome: 8 | - it successfully replaces unit tests for some tasks, like ensuring that the different parts of the app, remains in sync with each others. 9 | - it give you autocompletion 10 | 11 | That being said, if flow is misconfigured and you blindly trust the result, you can still have bug where it should have raised an alert for you. 12 | 13 | ## Checks 14 | 15 | - Every `*.js` file **should** have a comment on first line of the code with `// @flow`. 16 | - **Why?** 17 | - If it is not, the file is ignored by flow. 18 | - Every types **should** use **exact typing** `{|...|}` and not `{...}` 19 | - **What?** 20 | - https://flow.org/en/docs/types/objects/#toc-exact-object-types 21 | **Why?** 22 | - https://twitter.com/cpojer/status/780268582974296064 23 | 24 | ## Examples 25 | 26 | ### // @flow comment 27 | 28 | #### ✅ **Good example** 29 | 30 | ```js 31 | // @ flow 32 | 33 | import React from "react"; 34 | // rest of the file 35 | class Comp from React.Component { 36 | 37 | } 38 | ``` 39 | 40 | If you make a typo in `React`, you can see it, for instance. 41 | 42 | #### ⛔️ **Bad example** 43 | 44 | ```js 45 | import React from "react"; 46 | // rest of the file 47 | class Comp from React.Component { 48 | 49 | } 50 | ``` 51 | 52 | If you make a typo in `React`, you can don't see it. 53 | 54 | ### Exact object types 55 | 56 | #### ✅ **Good example** 57 | 58 | ```js 59 | // @ flow 60 | 61 | type PropsType = {| 62 | goats: Array 63 | |} 64 | 65 | const Herd = (props: PropsType) => ({props.goats.length}) 66 | ``` 67 | 68 | If you make a typo in `PropsType`, you can see it. 69 | 70 | #### ⛔️ **Bad example** 71 | 72 | ```js 73 | // @ flow 74 | 75 | type PropsType = { 76 | myFavoriteGoat: string 77 | } 78 | 79 | const Herd = (props: PropsType) => ({props.myFavoriteGoat}) 80 | ``` 81 | 82 | (`myFavoriteGoat` should raise an error, but don't since `PropsType` are not exact) 83 | -------------------------------------------------------------------------------- /git/merge-or-rebase-a-branch.md: -------------------------------------------------------------------------------- 1 | # [MO] (~ 2 min) 2 | 3 | ## Owner: Arthur Levoyer 4 | 5 | ## Prerequisites 6 | 7 | - Have git-cli installed 8 | 9 | ## Context 10 | 11 | Let's say, you are currently working on your own `my-feature` branch and someone from your team is merging new commits into the `master` branch which you would like to use. You have two different possibilities to incorporate them to your `my-feature` branch. 12 | 13 | ![image](https://wac-cdn.atlassian.com/dam/jcr:01b0b04e-64f3-4659-af21-c4d86bc7cb0b/01.svg?cdnVersion=fp) 14 | 15 | ## Steps 16 | 1. First approach: Merge a branch 17 | 18 | Merge the `master` branch into the `feature` branch by entering following: 19 | - `git checkout my-feature` 20 | - `git merge master` 21 | It gives you a new “merge commit” in the feature branch that ties together the histories of both branches. 22 | 23 | It's nice because it is a non-destructive operation but if your `master` branch is very active, you might quickly have a new commits including a lot of changes and this will pollute your history quite a lot. 24 | 25 | ![image](https://wac-cdn.atlassian.com/dam/jcr:e229fef6-2c2f-4a4f-b270-e1e1baa94055/02.svg?cdnVersion=fp) 26 | 27 | 28 | 2. Second approach: Rebase a branch 29 | - `git checkout my-feature` 30 | - `git rebase master` 31 | 32 |

The difference with the merge solution above, it that it will create new commits into your `my-feature` for each commits in `master` and give you a much cleaner project history.

33 | 34 | On the other hand, a very unwanted situation might occur if you do not follow the [Golden Rules of Rebasing](https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing) 35 | 36 | ![image](https://wac-cdn.atlassian.com/dam/jcr:5b153a22-38be-40d0-aec8-5f2fffc771e5/03.svg?cdnVersion=fp) 37 | 38 | Source: [atlassian.com](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) 39 | 40 | -------------------------------------------------------------------------------- /intro.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | post_title: '%TITLE%' 3 | layout: layout 4 | published: true 5 | categories: 6 | - '%CATEGORY%' 7 | --- 8 | -------------------------------------------------------------------------------- /javascript/promisify-a-callback.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Promisify a callback _(~5 min)_ 2 | 3 | ## Owner: Arthur Levoyer 4 | 5 | ## Why 6 | 7 | When an asynchronous action is performed, if you want to wait for its success or failure and avoid several chains of callbacks. See [callback hell](http://callbackhell.com/). 8 | 9 | ## Steps 10 | 11 | - I identified the asynchronous call I want to wait for 12 | - I included the function into a callback, the executor of the Promise will handle an asynchronous work (in the examples below the describeTable). Once the work is done, if it went well, we are calling the resolve function, if not we are calling the reject one. 13 | 14 | ## Examples 15 | 16 | ### Example 1: Bad example 17 | 18 | ```jsx 19 | export const waitForCallbackToBeSolved = () => { 20 | asynchronousAction(params, (error, data) => { 21 | // We create a Promise with the function using a callback in second arguments 22 | if (error) throw error; 23 | else console.log(data); 24 | }); 25 | }; 26 | 27 | export const getResponse = async () => { 28 | try { 29 | await waitForCallbackToBeSolved(); 30 | doStuff(); 31 | } catch (error) { 32 | log(error); 33 | throw error; 34 | } 35 | }; 36 | ``` 37 | 38 | -> [ ] Neither `Promise` nor `promisify` had been used hence we are sending a 200 status in all cases 39 | 40 | ### Example 2: Good example 41 | 42 | ```jsx 43 | export const waitForCallbackToBeSolved = () => { 44 | return new Promise((resolve, reject) => { 45 | // We create a Promise with the function using a callback in second argument 46 | asynchronousAction(params, (error, data) => { 47 | if (error) { 48 | return reject(error); 49 | } 50 | resolve(data); 51 | }); 52 | }); 53 | }; 54 | 55 | export const getResponse = async () => { 56 | try { 57 | await waitForCallbackToBeSolved(); 58 | doStuff(); 59 | } catch (error) { 60 | log(error); 61 | throw error; 62 | } 63 | }; 64 | ``` 65 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /ops/docker/deploy-with-https.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup HTTPS on your docker environment (~15 min) 2 | 3 | ## Owner: [Sammy Teillet](https://github.com/samox) 4 | 5 | ## Control points 6 | 7 | {% hint style='success' %} 8 | 9 | If, _as an expert of docker_, you want to adapt the standard to the context of your project, you have to check that: 10 | 11 | {% endhint %} 12 | 13 | * [ ] server listen port `80` and `443` 14 | * [ ] server redirect port `80` to `443` with `301: Moved Permanently` 15 | * [ ] proxy should renew certificates automatically before they expire (letsencrypt certificates have 90 days of validity) 16 | * [ ] you should get at least `A` when checking https://www.ssllabs.com/ssltest/ 17 | * [ ] you should check in the report of https://www.ssllabs.com/ssltest/ that the server devices range cover your definition of DONE: eg, if you need to support old IE or old android, you have to lower the security enough for compatibility 18 | 19 | ## Prerequisites 20 | 21 | * [ ] You have a staging environment with docker 22 | * [ ] You can ssh to the server 23 | * [ ] Make sure you have the docker rights `sudo usermod -aG docker $YOUR_USER_NAME` 24 | 25 | ## Steps (~15 min) 26 | 27 | ### Install the nginx-proxy companion (~5 min) 28 | 29 | - Connect to your server `ssh user@your.domain` 30 | - Clone the nginx-proxy-companion [project](https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion) on the server at the root of the server. 31 | 32 | ```bash 33 | git clone git@github.com:evertramos/docker-compose-letsencrypt-nginx-proxy-companion.git 34 | ``` 35 | 36 | - Create a `.env` file 37 | 38 | ```bash 39 | cd docker-compose-letsencrypt-nginx-proxy-companion 40 | cp ./.env.sample .env 41 | ``` 42 | 43 | - Set the `NGINX_FILES_PATH=/srv/nginx/data` in the `.env` 44 | - `vim ./.env` 45 | - line 41 replace `NGINX_FILES_PATH=/srv/nginx/data`(or a different path if you prefer) 46 | 47 | {% hint style='success' %} **CHECK** 48 | 49 | Try to launch the companion by running: 50 | 51 | ```bash 52 | ./start.sh 53 | ``` 54 | 55 | You should have the following error because the port 80 is already used by your app docker: 56 | 57 | ``` 58 | ERROR: for nginx-web Cannot start service nginx-web: driver failed programming external connectivity on endpoint nginx-web (4c0105fe57d370c99c0a143c967d1b8737006a4138618e1defebc4bab4e42d11): Bind for 0.0.0.0:80 failed: port is already allocated 59 | ``` 60 | 61 | ![](./docker-nginx-companion-error.png) 62 | 63 | {% endhint %} 64 | 65 | ### Configure your project to use the companion (~5 min) 66 | 67 | - Remove the binding 80 port command, but expose it 68 | 69 | ```diff 70 | version: '3' 71 | services: 72 | your-web-app: #It should contain port: "80:80" 73 | # ... 74 | - ports: 75 | - - "80:80" 76 | + expose: 77 | + - 80 78 | ``` 79 | 80 | - Configure the app to use the network created by the companion (`webproxy` is the default name) 81 | 82 | ```diff 83 | version: '3' 84 | services: 85 | # ... 86 | 87 | +networks: 88 | + default: 89 | + external: 90 | + name: webproxy 91 | ``` 92 | 93 | {% hint style='info' %} **GO FURTHER** 94 | 95 | https://blog.docker.com/2016/12/understanding-docker-networking-drivers-use-cases/ 96 | 97 | {% endhint %} 98 | 99 | - In your project set 3 environment variable: `VIRTUAL_HOST`, `LETSENCRYPT_HOST`, `LETSENCRYPT_EMAIL`. The email will be used by _Letsencrypt_ to notify you if the certificate expire.There are 2 ways: 100 | - In the docker-compose file 101 | - In your prod.env file that is read by your Dockerfile. 102 | 103 | 104 | {% hint style='info' %} **RECOMENDED WAY** 105 | 106 | Update the .env file of your web-app docker 107 | 108 | {% endhint %} 109 | 110 | - In the `./env/prod.env` add the following: 111 | 112 | ```diff 113 | #... other env variable 114 | + VIRTUAL_HOST=my.domain.cloud.bam.tech 115 | + LETSENCRYPT_HOST=my.domain.cloud.bam.tech 116 | + LETSENCRYPT_EMAIL=your@email.com 117 | ``` 118 | 119 | {% hint style='warning' %} **OTHER solution** 120 | 121 | If you have no .env file you an also Update the docker-compose-prod file 122 | 123 | 124 | ```diff 125 | version: '3' 126 | services: 127 | your-web-app: #It should contain port: "80:80" 128 | # ... 129 | environment: 130 | + - VIRTUAL_HOST=my.domain.cloud.bam.tech 131 | + - LETSENCRYPT_HOST=my.domain.cloud.bam.tech 132 | + - LETSENCRYPT_EMAIL=your@email.com 133 | ``` 134 | 135 | {% endhint %} 136 | 137 | 138 | ### Make the switch (~5 min) 139 | 140 | {% hint style='danger' %} **BUSINESS INTERRUPTION** 141 | 142 | You will have to shut down your docker (so the port 80 is available), so during this step your domain won't be accessible. 143 | 144 | {% endhint %} 145 | 146 | - Cut your app docker: 147 | 148 | ```bash 149 | cd your-project-directory 150 | docker-compose -f docker-compose-prod.yml down 151 | ``` 152 | 153 | - Start the companion (go to the companion directory): 154 | 155 | ```bash 156 | cd ../docker-compose-letsencrypt-nginx-proxy-companion 157 | ./start.sh 158 | ``` 159 | 160 | - Launch your project docker again: 161 | 162 | ```bash 163 | cd - 164 | docker-compose -f docker-compose-prod.yml up -d 165 | ``` 166 | 167 | {% hint style='success' %} **CHECK** 168 | 169 | - Check the validity of your domain, go to https://your.domain 170 | - Go [there](https://www.ssllabs.com/ssltest/) and check your domain. Useful tip: go to the __Handshake Simulation__ section and check the supported devices. 171 | 172 | {% endhint %} 173 | -------------------------------------------------------------------------------- /ops/docker/docker-nginx-companion-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/ops/docker/docker-nginx-companion-error.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-standards", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:bamlab/dev-standards.git", 6 | "author": "Yann Leflour ", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "echo 'No other tests'", 10 | "contributors:add": "all-contributors add", 11 | "contributors:generate": "all-contributors generate" 12 | }, 13 | "devDependencies": { 14 | "danger": "^1.0.0", 15 | "lodash": "^4.17.4", 16 | "minimatch": "^3.0.4" 17 | }, 18 | "dependencies": { 19 | "all-contributors-cli": "^4.4.0", 20 | "gitbook-plugin-ga": "^2.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /performance/backend/cache-routes-using-varnish.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Cache your routes using varnish (~45 min) 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Control points 6 | 7 | {% hint style='success' %} 8 | 9 | If, _as an expert of caching_, you want to adapt the standard to the context of your project, you have to check that: 10 | 11 | {% endhint %} 12 | 13 | * [ ] if the data is not in cache, you redirect to the backend 14 | * [ ] you don't cache authenticated requests 15 | * [ ] you don't cache requests that mutate data on the server (POST/PUT/DELETE in REST) 16 | * [ ] the cache duration is respecting business needs 17 | * example: a football game score can't be cached more than 1 min for a live match but can be cached forever for a finished game 18 | * [ ] you respect cache headers, like [Cache-Control](https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Cache-Control) 19 | 20 | ## Why 21 | 22 | Using a tool like Varnish to cache routes in order to optimize the response performance below 0.1s. 23 | 24 | ## Steps 25 | 26 | {% hint style='warning' %} 27 | 28 | **TODO** 29 | 30 | If you fall on this method of operation and want to fill it don't hesitate! It will help others. 31 | You ca for instance, look at this article https://medium.com/about-developer-blog/varnish-58c5d8269269 and convert it to a M33 like steps (short steps and check after after each steps) when you use it. 32 | 33 | A good MO example: [Setup HTTPS on your docker environment](../../ops/docker/deploy-with-https.mo.md) 34 | 35 | {% endhint %} 36 | -------------------------------------------------------------------------------- /performance/backend/how-to-investigate-performance.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] How to investigate back-end performance 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Why 6 | - You don't know from which end investigate the performance issue. Follow those steps to make sure you know how to investigate in an efficient way. Also in a way that you'll be able to share and explain your learnings easily with others. 7 | 8 | ## Steps 9 | - [ ] Define the context of the problem, and how to reproduce it 10 | - Ex: We want a performing player ranking query from a 3G network 11 | - [ ] Check the [performance tools and technics](./backend-performance-tools.md) before anything 12 | - [ ] Make sure you note every steps of your investigation, take screenshots and draw schemas 13 | - Ex: I requested the ranking route from Postman with the ID of a user => I changed the network in order to see the impact of bad internet on the request time => etc. 14 | - [ ] Start from the main broad problem, take screenshots and note your exact findings 15 | - Ex: The request takes 12,235s on an iPhone 4S on 3G network 16 | - [ ] Cut your problem in pieces as soon as you start going into details, and continue noting 17 | - Ex: The ranking request fetches data from the portfolio API in 10,123s and the user API in 1,856s 18 | - [ ] Make hypotheses: from 10,123s, you expect with your fix to reach below 1s because of a potential SQL query mis-performance 19 | - [ ] Fix it, and then compare the result with your initial state 20 | - If the issue is not fixed, write down that your hypotheses is invalid, it still is a learning and worth sharing 21 | - [ ] Update your product owner and team mates: by saving and sharing your final report, like in a Gist or Trello Ticket 22 | - [ ] Iterate... 23 | - [ ] Finally share what you learned with your team to be challenged and move forward! 24 | -------------------------------------------------------------------------------- /performance/backend/minimize-number-sql-queries.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Minimize your number of SQL queries 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Control points 6 | 7 | * [ ] you don't lazy load data that you need to access directly 8 | 9 | ## Why 10 | 11 | SQL is extremely powerful to retrieve and manipulate big chunks of data, but not powerful if you want to access data 1000 times in a row 12 | 13 | ## Steps 14 | 15 | - By default some ORMs lazy load relations: meaning that it will load the model you want but keep references of the relations 16 | - If you want to access those relations, it will only re-query the database later, in a loop for instance, it might send thousants of queries to retrieve relations 17 | - By join-loading the main SQL query, you make sure that you retrieve the relation directly, the ORM will join the model and its relations from the main first query 18 | - Then if you access one of the relation, it will already be available in the response object 19 | - See an example with SQL Alchemy: http://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html 20 | 21 | {% hint style='warning' %} 22 | 23 | **TODO** 24 | 25 | If you fall on this method of operation and want to fill it don't hesitate! It will help others. 26 | You ca for instance, look at this article https://medium.com/about-developer-blog/varnish-58c5d8269269 and convert it to a M33 like steps (short steps and check after after each steps) when you use it. 27 | 28 | A good MO example: [Setup HTTPS on your docker environment](../../ops/docker/deploy-with-https.mo.md) 29 | 30 | {% endhint %} 31 | -------------------------------------------------------------------------------- /performance/backend/output-sql-alchemy-orm-query.mo.md: -------------------------------------------------------------------------------- 1 | # Output SQL Alchemy ORM query 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Steps 6 | 7 | If your performance issues seem to come from your SQL queries, you should take a look at the output of SQL Alchemy: 8 | - Log/print the SQL Alchemy query: 9 | ```python 10 | logging.info(self.session.query( 11 | Revision.portfolio_id, 12 | func.max(Revision.valuation_date).label('max_date') 13 | )) 14 | ``` 15 | - Retrieve what's printed in your logs and format it: https://sqlformat.org/ 16 | - Test the query against the database to see how to optimize it 17 | -------------------------------------------------------------------------------- /performance/backend/python-investigation-tools.mo.md: -------------------------------------------------------------------------------- 1 | # Python investigation tools 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Steps 6 | 7 | ### Python: Simple and quick investigation of a function execution time 8 | 9 | By using timer functions you can see how long takes the execution: 10 | ```python 11 | import time 12 | import logging 13 | 14 | def retrieve_data(self, ids): 15 | # Start your time recording 16 | start_time = time.time() 17 | 18 | datas = Repository().get_data(ids).all() 19 | 20 | # First stop, to see how long took "get_data" 21 | first_record_time = time.time() 22 | logging.info('After getting datas: %s' % 23 | str(first_record_time - start_time)) 24 | 25 | answer = [{ 26 | 'id': str(data.id), 27 | 'related_data': data.relation.to_dict() if data.relation else {} 28 | } for data in datas] 29 | 30 | # Second stop, to see how long took the answer construction 31 | second_record_time = time.time() 32 | logging.info('After building the final answer: %s' % str(second_record_time - first_record_time)) 33 | 34 | return answer 35 | ``` 36 | 37 | ### Python: More advanced solution to profile the execution of a function and its children 38 | - https://blog.sicara.com/profile-surgical-time-tracking-python-db1e0a5c06b6 39 | 40 | ### Python: Further performance analysis tools 41 | You can also profile with the below tool: 42 | - https://docs.python.org/3/library/profile.html 43 | -------------------------------------------------------------------------------- /performance/backend/serve-images-as-static-files.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Save and serve your images as static files 2 | 3 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 4 | 5 | ## Control points 6 | 7 | * [ ] you don't save images in a database 8 | 9 | ## Why 10 | 11 | Save and serve the image file from the server in a specific folder, or an Amazon S3, instead of having it in the database in Base64 is much more performing for progressively displaying a page. 12 | 13 | ## Steps 14 | 15 | {% hint style='warning' %} 16 | 17 | **TODO** 18 | 19 | If you fall on this method of operation and want to fill it don't hesitate! It will help others. 20 | You ca for instance, look at this article https://medium.com/about-developer-blog/varnish-58c5d8269269 and convert it to a M33 like steps (short steps and check after after each steps) when you use it. 21 | 22 | A good MO example: [Setup HTTPS on your docker environment](../../ops/docker/deploy-with-https.mo.md) 23 | 24 | {% endhint %} 25 | -------------------------------------------------------------------------------- /performance/front/how-to-investigate-performance.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] How to investigate front-end performance 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | ## Why 6 | 7 | Sometimes, standards are not enough, or you're not applying them, resulting in performance bottlenecks. 8 | 9 | ## Steps 10 | 11 | Proceed by dichotomy: cut your app in multiple pieces in order to find out where the problem comes from. For example, literally remove some components or pages! 12 | 13 | ### Profile 14 | 15 | Measure the performance with the following tools. They're ordered by `simplicity` \* `perspectives of learnings`. 16 | 17 | - Put some `console.count('my component')` in your components' render methods in order to measure the number of renders 18 | - In app Performance monitor (CMD+D on iOS, CTRL+M on Android; and select Performance monitor) 19 | - Network profiler => if some calls are too long, investigate backend performance (TODO link to backend performance standard) 20 | - [React.unstable_Profiler](https://medium.com/@dave_lunny/how-to-use-reacts-experimental-new-profiler-feature-c340674e5d0e) 21 | - Close React Native Debugger and open the Chrome debugger. Click on the performance tab and hit record to start profiling the performance of your application. See [this article](https://building.calibreapp.com/debugging-react-performance-with-react-16-and-chrome-devtools-c90698a522ad) to learn how to read the output. 22 | - Native tools (Android Studio, XCode) 23 | - https://facebook.github.io/react-native/docs/performance.html#profiling 24 | - Inspect the JS<->native bridge with [RN-Snoopy](https://github.com/jondot/rn-snoopy) 25 | 26 | ## Useful links 27 | 28 | - React Native doc: https://facebook.github.io/react-native/docs/performance.html 29 | - Example commit: https://github.com/Minishlink/DailyScrum/commit/3c6d5f70a638a146f1a2158b94292010eb12186a 30 | -------------------------------------------------------------------------------- /performance/front/react-native-maps-performance.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] React Native Maps performance 2 | 3 | ## Owner: [Alban Depretz](https://github.com/chdeps) 4 | 5 | ## Why 6 | 7 | A lot of apps seem to be struggling with performance when it comes to maps & displaying pins. So I put together a REX that goes hand in hand with Louis Lagrange's standard on performance. You'll see that some generic tips given by Louis are applied here. It could be considered as an application of the standard by taking into account the specifics of maps' rendering. 8 | 9 | ## What 10 | 11 | Here are a few tips I suggest using to improve your maps' performance: 12 | 13 | 1. Use `onRegionChangeComplete` instead of onRegionChange. You will wait until you're done changing the region. 14 | 2. Use `debounce` when making HTTP calls to fetch new pins. 15 | 16 | > ⚠️ If a call takes longer than your wait time. Another call might triggered in the mean time & go through the callback before the first call. It might cause a laggy feeling & incoherent data. Cancelling debounce will not cancel async callback. You can do something like below : 17 | 18 | ```javascript 19 | //Generate a unique id to identify a query 20 | const queryId = shortid.generate(); 21 | this.queryId = queryId; 22 | this.setState({ isLoading: true }, () => 23 | this.props 24 | .callForPins(..args) 25 | .then(({ data: { pins } }) => { 26 | //There is a newest callback no need to update the state 27 | if (this.queryId !== queryId) return; 28 | return this.setState({ 29 | pins, 30 | }); 31 | }) 32 | .catch(console.warn) 33 | .finally(() => this.setState({ isLoading: false })) 34 | ); 35 | ``` 36 | 37 | 3. Use a `caching technique` to reduce the number of queries 38 | 39 | When calling for new pins, you need to consider what your inputs are. Given that you query pins on the map with the following arguments: 40 | * Region on which the user is (eg. You're located over Paris. You don't want to load a pin in New York) 41 | * Types of pins (eg. On a map, you just want the restaurants but you don't want to see the shops) 42 | * Search field (eg. If you're searching for bistrots you don't want to get Mc Donalds' pins) 43 | 44 | You can create a hash key with the type of pins & the search field : 45 | 46 | ```javascript 47 | //callForPins callback 48 | const key = extractKey({ pinTypes, search }); 49 | this.setState({ 50 | queriedRegion: { 51 | [key]: boundaries(region), 52 | }, 53 | pins, 54 | }); 55 | ``` 56 | 57 | Before querying for new pins on the map check whether the last query's hash key is the same & whether the region of your query is within the previous query's region. If so we already have the pins & there is no need for querying new pins. 58 | 59 | ```javascript 60 | _getPins = (region, pinTypes, search) => { 61 | //Create a uniq key out of the pinTypes & search parameters 62 | const key = extractKey({ pinTypes, search }); 63 | if (this.state.queriedRegion[key] 64 | && isRegionWithIn(boundaries(region), this.state.queriedRegion[key])) return; 65 | //Call for new pins with debounce if the pinTypes or search parameters have changed since last time 66 | //Or if the region queried is larger than the previous one 67 | return this._callForPinsDebounce(region, pinTypes, search); 68 | }; 69 | ``` 70 | 71 | > As regards to caching, there still things that need to be done. At the time, I was doing that Apollo did not provide any caching per parameter. It would allow us to have a greater history of calls in cache & only the last one. Also, ideally you could think of caching when zooming out. This involves some dev on the back-end, as it means that you are capable of fetching pins in a ring & only inside a cercle. 72 | 73 | 4. `shouldComponentUpdate` is your friend. Don't re-render the map if your pins haven't updated for instance. 74 | 5. Clustering of pins on the front end ([Clustering with react-native-maps](https://github.com/bamlab/react-native-components-collection/tree/master/packages/react-native-component-map-clustering)) 75 | 76 | -------------------------------------------------------------------------------- /performance/front/react-native-performance.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] React Native performance 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | ## Checks 6 | 7 | In order to have an efficient application from the get-go, respect the following standards. 8 | 9 | ## Why 10 | 11 | Because a few seconds of page render, a laggy chart display or a buggy animation makes you lose customers, those tips will help you avoid that before it even happens. 12 | In order to have an efficient application from the get-go, respect the following standards. 13 | 14 | ### General 15 | 16 | > Side note: You don't need to apply every of these standards right away (that would be premature optimization), but as your technical experience grows, you should adopt them along the way. These best practices are ordered by potential impact on performance. 17 | 18 | - Use an up-to-date version of your dependencies, and first and foremost: 19 | - [React Native](https://github.com/facebook/react-native/releases) 20 | - [React Navigation](https://github.com/react-navigation/react-navigation/releases) 21 | - Don't use images that are unnecessary big. Dynamic resizing is very inefficient on Android. Resize them to 1x 2x 3x flavors (`img.png`, `img@2x.png`, `img@3x.png`) and use them normally (`require('img.png')`). UX designers can export images easily this way with Sketch. 22 | - Use animations in order to make things more fluid (`animationEnabled` in `TabNavigator`; `LayoutAnimation`) 23 | - Use `shouldComponentUpdate` / `PureComponent`. Test thoroughly your component when using `shouldComponentUpdate` because this is error-prone. It will massively improve your app's performance though. 24 | - Don't create new functions on every render, [bind your functions efficiently](https://github.com/bamlab/dev-standards/blob/master/react-native/react/binding-functions-in-react-component.s.md). Similarly, avoid creating inline styles. 25 | - When using `Animated`, use [`useNativeDriver`](https://facebook.github.io/react-native/docs/animations.html#using-the-native-driver) 26 | - If you have a big view that has a lot of subviews, and these are not always shown to the user, use [`removeClippedSubviews`](https://facebook.github.io/react-native/docs/view.html#removeclippedsubviews) 27 | - When triggering a function after clicking on a button, or at `componentDidMount`, use [`InteractionManager.runAfterInteractions`](https://facebook.github.io/react-native/docs/interactionmanager.html) 28 | - Remove console logs from your production builds, use the [`transform-remove-console`](https://facebook.github.io/react-native/docs/performance.html#using-consolelog-statements) Babel plugin 29 | - When possible, use `Fragment` instead of `View` 30 | - Try to limit the number of data you display in charts, maps and tables. To investigate the potential impact, try to divide this number by 10 and measure the impact with the tools presented in the profiling section 31 | - Do not request your data to often: if it changes every hour, do not perform the same request every minutes, it will trigger useless renders and waste ressources. 32 | 33 | ### Useless renders 34 | 35 | - Use `shouldComponentUpdate` / `PureComponent`. Test thoroughly your component when using `shouldComponentUpdate` because this is error-prone. It will massively improve your app's performance though. 36 | - Don't create new functions on every render, [bind your functions efficiently](https://github.com/bamlab/dev-standards/blob/master/react-native/react/binding-functions-in-react-component.s.md). 37 | - Do not request your data to often: if it changes every hour, do not perform the same request every minutes, it will trigger useless renders and waste ressources. 38 | 39 | ### Displaying large list of elements 40 | 41 | - Try to limit the number of data you display in charts, maps and tables. To investigate the potential impact, try to divide this number by 10 and measure the impact with the tools presented in the profiling section 42 | - If you have a big view that has a lot of subviews, and these are not always shown to the user, use [`removeClippedSubviews`](https://facebook.github.io/react-native/docs/view.html#removeclippedsubviews) 43 | 44 | ### Technical debt 45 | 46 | - Use an up-to-date version of your dependencies, and first and foremost: 47 | - [React Native](https://github.com/facebook/react-native/releases) 48 | - [React Navigation](https://github.com/react-navigation/react-navigation/releases) 49 | - Remove console logs from your production builds, use the [`transform-remove-console`](https://facebook.github.io/react-native/docs/performance.html#using-consolelog-statements) Babel plugin 50 | - When possible, use `Fragment` instead of `View` 51 | 52 | ### Start-up times 53 | 54 | If your app takes too much time to initialize, solve the problem incrementally: 55 | 56 | 1. Add a splashscreen that [closes when the app is ready](https://github.com/Minishlink/DailyScrum/commit/811cfd57304dbb6f08386bce7b1d9d0b7c7388ae) with [`react-native-splash-screen`](https://github.com/crazycodeboy/react-native-splash-screen) 57 | 2. If the startup time is > 2 seconds, show a full page modal with an animation (in the continuity of your splashscreen) 58 | 3. If the startup time is consistently > 5 seconds (or 7 seconds with an animated splashscreen): if you have a very big app, implement [dynamic imports](https://facebook.github.io/react-native/docs/performance.html#unbundling-inline-requires); if not, look for other clues: aren't you doing some long API calls at startup? 59 | 60 | ## Exemple with maps in react-native 61 | 62 | You can take a look the following standard : [React native maps performance](https://github.com/bamlab/dev-standards/blob/master/performance/front/react-native-maps-performance.s.md) 63 | 64 | ## Good examples 65 | 66 | > Please andon and/or create an issue if you need one! 67 | 68 | ## Bad examples 69 | 70 | > Please andon and/or create an issue if you need one! 71 | -------------------------------------------------------------------------------- /performance/front/simulate-network-iphone.mo.md: -------------------------------------------------------------------------------- 1 | 2 | # iPhone: Simulate a network like LTE, 3G or Edge 3 | 4 | ## Owner: [Xavier Lefevre](https://github.com/xavierlefevre) 5 | 6 | ## Steps 7 | 8 | - Make sure you iPhone is connected to the Wifi network 9 | - Connect your computer to your iPhone via hotspot 10 | - Go to the iPhone settings 11 | - Then select: Developer > Network Link Conditioner 12 | - Enable 13 | - Select your desired network quality: LTE, 3G or Edge for instance 14 | - Test your requests in Postman and see the result on the performance 15 | > source: https://www.natashatherobot.com/simulate-bad-network-ios-simulator/ 16 | -------------------------------------------------------------------------------- /performance/front/table-and-chart-with-good-performance.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Optimize the render time of lists, tables and charts 2 | 3 | ## Owner: [Justine Mignot](https://github.com/justinemignot) 4 | 5 | ## Steps 6 | 7 | - [ ] Find how many lines or data points are displayed on your page 8 | - [ ] Decide if your end user should see them all 9 | - [ ] If it does not need all this data, try reducing its amount 10 | - [ ] If it does need all the data, see how to optimize render 11 | - Find solutions per components, "Charts, Maps, Tables and Lists" below 12 | 13 | ### Chart/Maps data optimization 14 | 15 | > Why: On a project like Investo for BNP, we reduced by 50% the data points of a chart, we saved 2s on the page render time! 16 | 17 | - Open your page with the chart 18 | - Reduce by half the quantity of data displayed 19 | - If the result is still clear enough for the end user, check how long it took to render (link to Thibaut's [article](https://blog.bam.tech/developper-news/5-tips-to-improve-performance-react-native-application) on how to investigate) 20 | - If the improvement is enough and the information clear for the user, perfect, you improved your performance and still helped your end user 21 | - If it's not enough, reproduce the previous step 22 | - If you can't manage to improve the performance and/or that you are now displaying too few points, you should investigate the render performance of your components, it means that the code is too heavy, not that you display too much data 23 | 24 | ### Tables/Lists render optimization 25 | 26 | > Why: On a project like Galaxy for BNP, they displayed 50 lines instead of ~500, and users never clicked on the 'see more' button! 27 | 28 | #### It does not need all this data, try reducing its amount 29 | 30 | - Open your page with the table 31 | - Reduce by half the quantity of lines displayed (or more if it makes more sense to display less on your project) 32 | - Add a 'See more' button to allow your user to display all lines 33 | - If the improvement is enough, perfect, you improved your performance 34 | - If it's not enough, reproduce the previous step 35 | - If you can't manage to improve the performance and/or that you are now displaying too few points, you should investigate the render performance of your components, it means that the code is too heavy, not that you display too much data 36 | 37 | #### If it does need all the data, see how to optimize render 38 | 39 | - Pagination: 40 | 41 | - Back: only if the cause of slowness is the time of backend request (more than 2s) 42 | - paginate, 43 | - use lazy loading. 44 | - Front: if you want to minize elements to display and so gain on rendering time. 45 | 46 | - React Native Components: 47 | 48 | - use [FlatList](https://facebook.github.io/react-native/docs/flatlist), [SectionList](https://facebook.github.io/react-native/docs/sectionlist), [VirtualizedList](https://facebook.github.io/react-native/docs/virtualizedlist) 49 | because it render items lazily, just when they are about to appear, and removes items that scroll way off screen to save memory and processing time 50 | - DO NOT USE `ScrollView`: it simply renders all its react child components at once, so it has a big performance downside! 51 | 52 | - React library: 53 | - [react-virtualized](https://bvaughn.github.io/react-virtualized/#/components/List): 54 | great tool to display a big list with React. 55 | But implementing it add a not negligible complexity on your tickets. Take it into consideration. 56 | -------------------------------------------------------------------------------- /performance/performance-decision-flow.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Performance Decision Flow 2 | 3 | ## Owner: [Thibaut Guedou](https://github.com/thibautguedou3) 4 | 5 | ## Checks 6 | 7 | - Back-end: 8 | - Your requests take less than 500ms to complete 9 | - Front-end: 10 | 11 | - Your pages take less than 2s to display 12 | - No visible lags 13 | - Every action has instant visual feedback 14 | 15 | ## Do you know how to investigate your performance issues? 16 | 17 | ### General 18 | 19 | 1. [Simulate a network like LTE, 3G or Edge with an iPhone in hotstop](/performance/front/simulate-network-iphone.mo.md) 20 | 21 | ### Backend 22 | 23 | 1. [How to investigate a general backend performance issue](/performance/backend/how-to-investigate-performance.mo.md) 24 | 2. [Python investigation tools](/performance/backend/python-investigation-tools.mo.md) 25 | 3. [Output SQL Alchemy ORM query](/performance/backend/output-sql-alchemy-orm-query.mo.md) 26 | 27 | ## Now that you investigated, can we help you? 28 | 29 | ### Backend 30 | 31 | 1. [Cache your routes using varnish](/performance/backend/cache-routes-using-varnish.mo.md) 32 | 2. [Serve images as static files](/performance/backend/serve-images-as-static-files.mo) 33 | 3. [Minimize your number of SQL queries](/performance/backend/minimize-number-sql-queries.mo.md) 34 | 35 | ### Front 36 | 37 | 1. [Build performant application](/performance/front/react-native-performance.s.md) 38 | 2. [Investigate performance issues](/performance/front/how-to-investigate-performance.mo.md) 39 | 3. Pure components **-> [To do]** 40 | 4. [Optimize the render time of lists, tables and charts](/performance/front/table-and-chart-with-good-performance.mo.md) 41 | 42 | ## Have you a mean of keeping the performance under control? 43 | 44 | ### Backend 45 | 46 | - [Monitor your backend performance with K6](https://github.com/bamlab/performance-monitoring) 47 | 48 | ## Good examples 49 | 50 | > Please andon and/or create an issue if you need one! 51 | 52 | ## Bad examples 53 | 54 | > Please andon and/or create an issue if you need one! 55 | -------------------------------------------------------------------------------- /project-standards/project-success/index.md: -------------------------------------------------------------------------------- 1 | # Project Success 2 | 3 | ### Objective 4 | > Succeeding to accompany our client, by making this project a success for them and for BAM 5 | 6 | ### Standards 7 | - Critère de succès connus, BAMED et mesurés (fixé avec le payeur) 8 | - Projet réussi sur le questionnaire BAM 9 | - Sponsor présent à la review ou à la précédente 10 | - Sprint réussi (Sprint Goal atteint et nombre de points >= forecast) 11 | - [Launch in production (each plateform) or Apple agreement asked (if sprint >= 2)](/project-standards/project-success/production.s.md) 12 | - Aucune dépendance extérieure 13 | - PO valide sur plateforme ISO prod (provisioning + data synchronisée hebdomadairement) 14 | - Aucune régression ou bug découvert lors du dernier sprint (si oui, créer un bac rouge) 15 | - Aucun ticket "à valider" revenu en doing (si oui, créer un bac rouge) 16 | 17 | ### Todo 18 | 1. Translate the standards 19 | -------------------------------------------------------------------------------- /project-standards/project-success/production.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Launch in production (each platform) or Apple agreement asked (if sprint >= 2) 2 | 3 | ## Owner: [Xavier Lefèvre](https://github.com/xavierlefevre) 4 | 5 | ## Description 6 | - The project needs to be placed in front of its end users as fast as possible, the team needs to do the necessary for this as soon as the second sprint starts. 7 | 8 | ## Impact 9 | - Showing a MVP as soon as possible to end users is a mean to avoid as much as possible waste and un-necessary developments (hence spendings) for un-wanted features. The risk to waste a lot of money in developments is much higher than the risk to be repelled by your customer base for a small clean feature. 10 | 11 | ## Checks 12 | - [ ] Every week, take a "Launch in production" ticket. 13 | - [ ] Create a "In production" ticket in your "Done" columns in order to specify which dev has been launched or not, and keep a trace of it for future "LIP". 14 | - [ ] For micro-services, check each and every API when you launch in production. 15 | - [ ] When developing, always think about the retro-compatibility of your changes in the back-end (versioning) to make sure that people who do not have the last version of the app don't get un-wanted bugs. 16 | 17 | ## Bad Examples 18 | *TBD* 19 | 20 | ## Good Examples 21 | *TBD* 22 | -------------------------------------------------------------------------------- /project-standards/taking-over-project/index.md: -------------------------------------------------------------------------------- 1 | # Taking over an existing project 2 | 3 | ### Objective 4 | 5 | > Minimize malfunction risks when starting to work on a project that had been started outside of BAM 6 | 7 | 8 | ### Steps 9 | 10 | - [Migrate certificates to BAM's repository](/project-standards/taking-over-project/migrate-to-new-ios-certificates.mo.md) 11 | -------------------------------------------------------------------------------- /project-standards/taking-over-project/migrate-to-new-ios-certificates.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Migrate project to BAM's certificates 2 | 3 | ## Owner: [Yassine Chbani](https://www.github.com/yassinecc) 4 | 5 | ## Description 6 | - A project started by a third party should use your organisation's iOS certificates once a team starts working on it 7 | 8 | ## Impact 9 | - If the original Apple developer account is not an entreprise one, the certificates for the staging app will expire and renewing them will include a dependency to the original owner of the project 10 | 11 | ## Prerequisites 12 | - [ ] You have access to the previous Apple Developer account 13 | - [ ] Your project uses fastlane 14 | 15 | ## Steps 16 | ### Removing the old app ID 17 | - Log in to the original Apple Developer account on developer.apple.com 18 | - Go to the certificates, Identifiers and Profiles page 19 | - In the Identifiers section, locate the staging app's ID and select it 20 | - In the menu you opened, browse to the bottom and delete the app ID 21 | 22 | ### Generating a new provisiong profile 23 | - In your fastlane folder, open the .env file for the staging app 24 | - Change the `MATCH_GIT_URL` and `IOS_TEAM_ID` fields to the ones used by you organisation 25 | - If not already the case, set `MATCH_TYPE` to 'enterprise' and `MATCH_FORCE_ENTERPRISE` to '1' in the `IOS_MATCH` section of the env file 26 | - Change the `FL_HOCKEY_API_TOKEN` to you organisation's token 27 | - Run `bundle exec fastlane ios setup --env=staging` 28 | - Deploy to HockeyApp 29 | - Download and test the app 30 | - Share with your product owner and relevant people the new link to download the staging app 31 | -------------------------------------------------------------------------------- /project-standards/technical-agility/code-vocabulary-identical-business-vocabulary.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Code's vocabulary identical to business' vocabulary 2 | 3 | ## Owner: TBD 4 | 5 | ## Description 6 | - The name of your code variables and DB entities should represent as much as possible the language used by the client/business side. 7 | 8 | ## Impact 9 | - The communication between the PO and the technical team as much as the arrival of a new developer on the project are made easier. 10 | 11 | ## Checks 12 | *TBD* 13 | 14 | ## Bad Examples 15 | *TBD* 16 | 17 | ## Good Examples 18 | *TBD* 19 | -------------------------------------------------------------------------------- /project-standards/technical-agility/index.md: -------------------------------------------------------------------------------- 1 | # Technical Agility 2 | 3 | ### Objective 4 | > Ensuring a sustainable and easy to takeover technical project 5 | 6 | ### Standards 7 | - [Code's vocabulary identical to business' vocabulary](/project-standards/technical-agility/code-vocabulary-identical-business-vocabulary.s.md) 8 | - [Stable and up-to-date dependencies](/project-standards/technical-agility/up-to-date-dependencies.s.md) 9 | - [This week, a member of the technical team destroyed their project environment and re-installed everything under 15minutes](/project-standards/technical-agility/under-15-minutes-project-installation.s.md) 10 | - At least one re-factoring per day 11 | - [The tests' standards of my framework are respected](/project-standards/technical-agility/react-native-test.s.md) 12 | - Build and deployment in one command (staging and production) 13 | - Green continuous integration on every deployed branches 14 | -------------------------------------------------------------------------------- /project-standards/technical-agility/under-15-minutes-project-installation.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] This week, a member of the technical team destroyed their project environment and re-installed everything under 15minutes 2 | 3 | ## Owner: *TBD* 4 | 5 | ## Description 6 | - In order to make sure that a new BAMer or even an external team could easily without too much waste install the project and start creating value under 15 minutes. 7 | 8 | ## Impact 9 | - If the team does not regularly kill and re-install the project, in order to update the install process documentation, there is a high risk that a new developer arriving on the project spends several hours to try to launch the project on its machine. 10 | 11 | ## Checks 12 | *TBD* 13 | 14 | ## Bad Examples 15 | *TBD* 16 | 17 | ## Good Examples 18 | *TBD* 19 | -------------------------------------------------------------------------------- /project-standards/technical-agility/up-to-date-dependencies.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Stable and up-to-date dependencies 2 | 3 | ## Owner: [Xavier Lefèvre](https://github.com/xavierlefevre) 4 | 5 | ## Description 6 | - The project code dependencies such as NPM packages need to be very regularly updated, the team should check once per sprint. 7 | 8 | ## Impact 9 | - An old dependency, can become deprecated and not adapt well with new operating systems, browsers, development tools, etc. 10 | 11 | ## Checks 12 | *TBD* 13 | 14 | ## Bad Examples 15 | 16 | ### redacted project 17 | - Problem: Cordova has not been updated for several months on the project and one day a developer could not launch the app on an iOS emulator because their new Xcode version had an emulator list discrepancy not handled by this old Cordova version, but handled by the new one. 18 | - Loss: 1h30 of debugging + 1h30 of problem solving 19 | 20 | ## Good Examples 21 | *TBD* 22 | 23 | ## Upgrading React Native 24 | 25 | React Native is very probably the dependency you'll have to upgrade the more often so we felt it was worth dedicating a [whole article](../../react-native/update/upgrade-react-native.mo.md) to it. 26 | -------------------------------------------------------------------------------- /react-native/animations/react-native-animations.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] React Native animations 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | ## Why 6 | 7 | Animations are a great way to improve the User Experience by : 8 | 9 | - easing the interactions, so that they may seem more natural 10 | - giving a clear visual feedback on an action's success or failure, and on its nature (tap / long press / slide / pinch) 11 | - educating the user on possible interactions on an element 12 | - helping the user wait while content is loading 13 | - giving a sensation of speed and minimizing small performance problems 14 | - show more content on the page in a fluid manner 15 | 16 | ## Checks 17 | 18 | - In order to improve how we create animations, I send a quick message to the owner "Hey it's , I'm going to make an animation on " 19 | - Creating an animation should not take more than half a day of design and development time. If it takes more time, I andon the owner who will help me or find someone to help me. 20 | - I know of the resources that can help me: 21 | - similar animations that BAM made previously with the [catalog](link available on M33 standard): this can help me showcase examples for my PO, estimate the time needed for the design and the development, and find a technical strategy for my animation 22 | - the tools that are available in order to create a React Native animation with [this table](https://github.com/bamlab/animations/blob/master/matrix.md) 23 | - the [official guide](https://facebook.github.io/react-native/docs/animations) on animations 24 | - If I use `Animated`: 25 | - I make sure to use the parameter `useNativeDriver` in `Animated.timing`, `Animated.spring`, `Animated.decay` and `Animated.event` (see [React Native performance](../../performance/front/react-native-performance.s.md)) 26 | - I only use `Animated.interpolate` on styles of type `opacity` or `transform` (otherwise `useNativeDriver` won't work) 27 | - If I use any third party library, I look at the documentation and/or code, and if a `useNativeDriver` prop exists, I use it 28 | - When my animation is finished, I make a GIF of it (with [Kap](https://getkap.co/): `brew cask install kap`) and add it to the catalog [here](https://github.com/bamlab/animations/blob/master/catalog.md) (10 min) 29 | 30 | ## Good examples 31 | 32 | > Please andon and/or create an issue if you need one! 33 | 34 | ### `useNativeDriver` 35 | 36 | ```js 37 | 45 | ``` 46 | 47 | ### Style interpolation 48 | 49 | ```js 50 | 60 | ``` 61 | 62 | ## Bad examples 63 | 64 | > Please andon and/or create an issue if you need one! 65 | 66 | ### `useNativeDriver` 67 | 68 | ```js 69 | 75 | ``` 76 | 77 | ### Style interpolation 78 | 79 | ```js 80 | 89 | ``` -------------------------------------------------------------------------------- /react-native/architecture/default-stack.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] React Native stack 2 | 3 | ## Owner: Florian Rival 4 | 5 | ## Why 6 | 7 | Having a set of common tools/libraries allow us to quickly move from one project to another, be more efficient and get a deep knowledege of each tool, including the advantages **and** drawbacks of these tools. 8 | 9 | ## I need a navigation solution to start my app 10 | 11 | | Library/tool | What | Why | 12 | | ------------- |-------------| -----| 13 | | `react-navigation` | Navigation library for React Native | Recommended solution on [React Native documentation](https://facebook.github.io/react-native/docs/navigation.html), very customizable and with performant good enough for +90% of apps | 14 | | `react-native-navigation` | Navigation implemented using native navigation components | If you need to use the native navigation components so that your navigation is 100% equivalent to a native app. | 15 | 16 | ## I need to display icons 17 | 18 | | Library/tool | What | Why | 19 | | ------------- |-------------| -----| 20 | | `react-native-vector-icons` | Customizable Icons for React Native | Most comprehensive library of icons | 21 | 22 | ## I want to store the state of the app 23 | 24 | | Library/tool | What | Why | 25 | | ------------- |-------------| -----| 26 | | `redux` | State container | Widely used in the React community, lots of dev tooling | 27 | | `redux-persist` | Persistence of the data of the app | Simple to integrate with Redux and flexible | 28 | | `redux-saga` | Handling of asynchronous processes in the app | Simple to integrate with Redux, testable, make complex business flow easy to read | 29 | 30 | ## I want to handle forms 31 | 32 | | Library/tool | What | Why | 33 | | ------------- |-------------| -----| 34 | | `formik` | Forms handling library | | 35 | | `date-fns`, `moment` | Date handling library | Widely used libraries. | 36 | 37 | ## I want to handle animations 38 | 39 | | Library/tool | What | Why | 40 | | ------------- |-------------| -----| 41 | | Lottie (`react-native-lottie`) | Animation library | Good looking, performant animations. Easy to integrate in React Native. | 42 | 43 | ## I need to add testing to my app 44 | 45 | | Library/tool | What | Why | 46 | | ------------- |-------------| -----| 47 | | jest | Testing framework | Already integrated with React Native by default, allow to do snapshot testing | 48 | | redux-saga-test-plan | Testing utilities for `redux-saga` | Allow to test redux-saga | 49 | | Detox | End to end testing | Alternatives (Calabash) are not reliable enough and slow | 50 | 51 | ## I want static type checking 52 | 53 | | Library/tool | What | Why | 54 | | ------------- |-------------| -----| 55 | | Flowtype | Static typing | Improve developer experience, help to avoid errors when dealing with complex objects | 56 | | Typescript | Static typing | More stable and mature than Flowtype. Never used on a React Native project at BAM yet | 57 | 58 | ## I want to ensure the quality of my codebase 59 | 60 | | Library/tool | What | Why | 61 | | ------------- |-------------| -----| 62 | | Prettier | Automatic formattting of source code | Avoid all discussions/loss of time on styling Integration/plugin for editors | 63 | | eslint with `eslint-config-universe` | Linter | | 64 | 65 | ## I want to be able to push on-the-fly updates to my app 66 | 67 | | Library/tool | What | Why | 68 | | ------------- |-------------| -----| 69 | | AppCenter Code Push | Deploy mobile app updates without a full release | Speed up the deployment during development, allow for bug fixes and new features releases on the fly | 70 | 71 | ## I need to have crash/bugs reporting 72 | 73 | | Library/tool | What | Why | 74 | | ------------- |-------------| -----| 75 | | Sentry | Native crashes and JS exceptions reports | Easy integration and good React Native support with `react-native-sentry` | 76 | 77 | ## I need to add analytics/tracking to my app 78 | 79 | | Library/tool | What | Why | 80 | | ------------- |-------------| -----| 81 | | Firebase Analytics | Analytics for mobile applications | The recommended solution by Google for applications, with a good quality native module for React Native ([react-native-firebase](https://github.com/invertase/react-native-firebase)) | 82 | 83 | ## I need to have automated deployment 84 | 85 | | Library/tool | What | Why | 86 | | ------------- |-------------| -----| 87 | | fastlane | Automation of deployment tasks | Most widely used deployment tool for iOS/Android + existing actions for HockeyApp/App Center/App Store/Play Store | 88 | | match | Automation of certificate/provisioning profile generation | Avoid dealing with certificates, one place storage for all certificates | 89 | 90 | ## I need a continuous integration service for launching tests 91 | 92 | | Library/tool | What | Why | 93 | | ------------- |-------------| -----| 94 | | Bitrise | Automation of applications builds | Ensure reproducible builds, avoid spending time building apps on developer computers | 95 | | Travis CI | Automation of unit tests | | 96 | 97 | -------------------------------------------------------------------------------- /react-native/architecture/file-naming.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] File Naming 2 | 3 | ## Checks 4 | 5 | - File names use [camelCase](https://en.wikipedia.org/wiki/Camel_case) 6 | - Component File names use [PascalCase](https://en.wikipedia.org/wiki/PascalCase) and use the component name 7 | - When containers are next to their component 8 | - The component name should be `.component.js` 9 | - The container name should be `.container.js` 10 | -------------------------------------------------------------------------------- /react-native/architecture/project-architecture.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Project Architecture 2 | 3 | ## Checks 4 | - Entire code except `index.ios.js` and `index.android.js` should be in `src` 5 | - The structure should be as follow 6 | - `index.ios.js` 7 | - `index.android.js` 8 | - `/src` 9 | - `modules` 10 | - `components` 11 | - `pages` 12 | - `style` 13 | -------------------------------------------------------------------------------- /react-native/debugging/analyse-bug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/debugging/analyse-bug.jpg -------------------------------------------------------------------------------- /react-native/debugging/debug-javascript-ios-device.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug Javascript on an iOS Device 2 | 3 | ## Prerequisites 4 | - [ ] Have [React Native Debugger](https://github.com/jhen0409/react-native-debugger) installed 5 | - [ ] Have a certificate + provisioning profile for debug 6 | 7 | ## Steps 8 | 9 | > :warning: The Ip mentioned is the one from your local network and not your router's ip on the internet 10 | 11 | > :warning: Make sure you have reverted the changes after debugging to avoid build issues 12 | 13 | - Make sure both the device and computer are on the same network 14 | - Open your project in Xcode 15 | - In `Libraries/React.xcodeproj/React/Base/RCTBundleURLPRovider.m` 16 | - Replace `NSString *host = ipGuess ?: @"localhost";` 17 | - With your computer IP address (remove ipGuess if it poses problems): `NSString *host = @"";` 18 | - In `Libraries/RCTWebSocket.xcodeproj/RCTWebSocketExecutor.m` 19 | - Replace: `host = @"localhost";` 20 | - With your computer IP address: `host = @"";` 21 | - Verify signing by making sure you have obtained the signing certificate for debug first 22 | - Run the app 23 | - Shake to open the menu 24 | - Hit *Debug Remotely* 25 | -------------------------------------------------------------------------------- /react-native/debugging/debug-javascript.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug Javascript Code *(~3 min)* 2 | 3 | ## Owner: Yann Leflour 4 | 5 | ## Prerequisites *(~5 min)* 6 | - [ ] Have [React Native Debugger](https://github.com/jhen0409/react-native-debugger) installed 7 | 8 | ## Steps *(~3min)* 9 | 10 | - Have your app running on a simulator or device *(2min)* 11 | - ***Check:*** The simulator opens 12 | - Connect debugging *(~1min)* 13 | - Open **React Native Debugger** 14 | - Shake your device to open the development menu 15 | - Press `Debug JS Remotely` 16 | - Your app should now be connected to your debugger 17 | - ***Check:*** You can see the source code in React Native Debugger's *sources* pane 18 | 19 | -------------------------------------------------------------------------------- /react-native/debugging/debug-native-android.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug on Android 2 | 3 | ## Owner: [Yann Leflour](https://github.com/yleflour) 4 | ## Debug Android code 5 | ### Prerequisites 6 | 7 | - [ ] Have [Android Studio](https://developer.android.com/studio/index.html) installed 8 | ## Steps 9 | 10 | - Open **Android Studio** 11 | - Click on *File > Open* 12 | - Navigate to and open */android* 13 | - Run in debug mode from **Android Studio** 14 | 15 | ## Log Android Errors 16 | 17 | ### Prerequisites 18 | 19 | - [ ] Have [Pidcat](https://github.com/JakeWharton/pidcat) installed 20 | 21 | ## Steps 22 | 23 | - Connect the Android phone to your computer 24 | - Make sure that the phone is available from your computer: `adb devices` 25 | - Run: `pidcat ` 26 | - See all the logs related to your app, requests, native and javascript logs and errors, etc. 27 | -------------------------------------------------------------------------------- /react-native/debugging/debug-native-ios.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug Native iOS Code 2 | 3 | ## Prerequisites 4 | 5 | - [ ] Have [XCode installed](https://developer.apple.com/xcode/) installed 6 | 7 | ## Steps 8 | 9 | 1. From your project root run `open ios/.xcworkspace` if the file exists or `open ios/.xcodeproj` 10 | 2. In Xcode press the play button to run in debug mode 11 | -------------------------------------------------------------------------------- /react-native/debugging/debug-network-calls.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug Network calls in React Native debuggers *(~1 min)* 2 | 3 | ## Owner: Alexandre Moureaux 4 | 5 | ## Prerequisites *(~5 min)* 6 | - [ ] You know how to [debug javascript](/react-native/debugging/debug-javascript.mo.md) 7 | 8 | ## Steps *(~1min)* 9 | 10 | - Follow https://github.com/bamlab/react-native-debugger-utils#installation 11 | - Add this code in your app: https://github.com/bamlab/react-native-debugger-utils#debug-network-calls 12 | - Refresh your page with the React Native debugger open and go the network tab 13 | - [Star the repo](https://github.com/bamlab/react-native-debugger-utils) if it works ;) 14 | -------------------------------------------------------------------------------- /react-native/debugging/debug-two-ios-apps-simultaneously.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug two iOS apps simultaneously 2 | 3 | ## Owner: [Xavier Lefèvre](https://www.github.com/xavierlefevre) 4 | 5 | ## Last update date: 29th of January 2018 6 | 7 | ### Prerequisites 8 | 9 | * Having already installed and launched each app before 10 | * Using [React Native Debugger](https://github.com/jhen0409/react-native-debugger) 11 | 12 | ### Steps 13 | 14 | * For the first application, you can launch it as you would normally: 15 | 16 | * Open it with xcode `xcode ./ios/your-first-app.xcworkspace` 17 | * Open React Native Debugger 18 | * Launch the simulator from xcode with the play button on the top left 19 | 20 | {% hint style='success' %} **CHECK** 21 | In RN debugger, you can see logs from the first app. 22 | {% endhint %} 23 | 24 | * For the second application: 25 | 26 | * Open it with xcode `xcode ./ios/your-second-app.xcworkspace` 27 | * Modify the port used by the packager in the native code: 28 | * Do a full project search with "Maj + Cmd + F" 29 | * Look for "8081" 30 | * Replace "8081" by a new port, like "9980" 31 | * Save each change 32 | * In React Native Debugger: 33 | * Open a new window "Cmd + T" 34 | * Enter your new port "9980" 35 | * Launch the simulator from xcode with the play button 36 | * Launch a new packager from your project directory: `react-native start --port 9980` 37 | * Close and re-open the app from within the simulator 38 | 39 | {% hint style='success' %} **CHECK** 40 | In the other RN debugger, you can see logs from the second app. 41 | {% endhint %} 42 | 43 | * You are good to go! Both apps are now running with separate packagers and separate debuggers. 44 | -------------------------------------------------------------------------------- /react-native/debugging/debug-webviews.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Debug a React Native WebView *(~5 min)* 2 | 3 | ## Owner: [Louis Zawadzki](https://github.com/louiszawadzki) 4 | 5 | ## Prerequisites 6 | - [ ] A React Native application with a WebView 7 | - [ ] Safari if you want to debug on an iOS device 8 | 9 | ## Steps iOS *(~2min)* 10 | 11 | - Run your app on your iOS simulator 12 | - Open Safari 13 | - Enable the "Develop" menu: 14 | - Pull down the "Safari" menu and choose "Preferences" 15 | - Click on the "Advanced" tab 16 | - Check the box next to "Show Develop menu in menu bar" 17 | - Pull down the "Develop" menu 18 | - Click on "Simulator" that should be right below your computer 19 | - Select your WebView in the menu 20 | 21 | ## Steps Android *(~3min)* 22 | 23 | - In your `android/app/src/main/java/com/applilabchatbot/MainApplication.java` add `import android.webkit.WebView;` in the imports and the following line in your `onCreate` method: 24 | 25 | ```java 26 | @Override 27 | public void onCreate() { 28 | super.onCreate(); 29 | SoLoader.init(this, /* native exopackage */ false); 30 | + WebView.setWebContentsDebuggingEnabled(true); 31 | } 32 | ``` 33 | 34 | - Launch your app 35 | - Open Chrome 36 | - Go to [chrome://inspect](chrome://inspect) 37 | - Select your WebView under your device name 38 | 39 | ### Common WebViews pitfalls 40 | 41 | - To inject Javascript you have to set your WebView's `javaScriptEnabled` prop to `true` 42 | - On Android, you can't use arrow functions in the injected javascript 43 | - `window.postMessage` might not be available straight away in your injected Javascript, to make sure it's the case you can wrap your injected code by a setTimeout like this: 44 | 45 | ```js 46 | setTimeout(function() { 47 | /* your injected js goes here */ 48 | }, 0) 49 | ``` 50 | -------------------------------------------------------------------------------- /react-native/debugging/get-ios-logs.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Get iOS Logs 2 | 3 | ## Owner: [Julien Nassar](https://github.com/juliennassar) 4 | 5 | ## Why / context 6 | 7 | If you want to debug native modules (crash, production app...) it is important to have logs. This standard is a way to get them. 8 | 9 | ### Prerequisites 10 | 11 | - [ ] Have Xcode installed 12 | 13 | ## Steps 14 | 15 | - Open **Xcode** 16 | - Click on *window > Devices and simulators* 17 | - Select your device and open logs in the window 18 | 19 | {% hint style='success' %} **CHECK** 20 | 21 | ![demo](https://user-images.githubusercontent.com/13121639/37330274-d7b88264-26a0-11e8-8151-2cbf09d01ef0.gif) 22 | 23 | {% endhint %} 24 | -------------------------------------------------------------------------------- /react-native/debugging/handle-gradle-dependencies-clash.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Handle version conflicts between Gradle dependencies 2 | 3 | ## Owner: [Louis Zawadzki](https://github.com/louiszawadzki) 4 | 5 | ## Prerequisites: 6 | 7 | You have 2 dependencies that depend on different versions of the same package, so your Android build will fail with an error like: 8 | 9 | ``` 10 | com.android.dex.DexException: Multiple dex files define google/android/play-services/iid/R$anim; 11 | ``` 12 | 13 | If you're having an issue with Google Maps and Firebase you can have a look at [this article](https://medium.com/@suchydan/how-to-solve-google-play-services-version-collision-in-gradle-dependencies-ef086ae5c75f). 14 | 15 | ## Steps: 16 | 17 | ### 1. Find out which dependencies is causing the issue *(~2min)* 18 | 19 | First, you need to figure out which package is causing the dependency issue. 20 | 21 | Versions conflicts break your build when one method is present in one version and not in another. 22 | 23 | **N.B.**: You can potentially have versions conflicts without any issue. Similarly your build can pass but you can still have issues if the logic inside a function has been changed. 24 | 25 | Look at your error to know exactly which package is causing the issue (in the previous example it's probably `com.google.android.gms:play-services-iid`) 26 | 27 | Then you must find out which version should be set. 28 | 29 | To do so, run `cd android && ./gradlew app:dependencies && cd ..`. 30 | This will print out your tree of dependencies. 31 | 32 | In there, you must look for two instances of the dependency but with different versions. 33 | For example if it returns: 34 | 35 | ``` 36 | _debugCompile - ## Internal use, do not manually configure ## 37 | +--- project :react-native-maps 38 | | +--- com.google.android.gms:play-services-base:11.1.6 39 | | \--- com.google.android.gms:play-services-basement:11.1.6 40 | +--- com.salesforce.marketingcloud:marketingcloudsdk:5.3.+ -> 5.3.1 41 | | +--- com.google.android.gms:play-services-gcm:11.0.1 -> 11.1.6 42 | | | \--- com.google.android.gms:play-services-iid:11.0.1 -> 11.1.6 43 | | | +--- com.google.android.gms:play-services-base:11.0.1 -> 11.1.6 (*) 44 | | | \--- com.google.android.gms:play-services-basement:11.0.1 -> 11.1.6 (*) 45 | ``` 46 | 47 | A quick note on how to read this tree: 48 | 49 | - `(*)` indicates that this lib is already installed higher in the tree 50 | - `->` indicates that a different version of the library was installed 51 | 52 | You can see here for example that `com.google.android.gms:play-services-base` version is 11.1.6 for `react-native-maps` and version 11.0.1 for `com.salesforce.marketingcloud:marketingcloudsdk`, therefore forcing `com.google.android.gms:play-services-iid` to be at version 11.1.6. 53 | 54 | 55 | ### 2. Force dependency to use a specific version 56 | 57 | If the highest version of the dependency does not work, your next guess has to be the lowest one required by your dependencies. 58 | 59 | So now in your `android/app/build.gradle` you need to: 60 | 61 | - Tell `react-native-maps` not to compile `com.google.android.gms:play-services-base`: 62 | ``` 63 | compile(project(':react-native-maps')){ 64 | exclude group: 'com.google.android.gms', module: 'play-services-base' 65 | } 66 | ``` 67 | 68 | - Tell `com.salesforce.marketingcloud:marketingcloudsdk` not to compile `com.google.android.gms:play-services-base`: 69 | ``` 70 | compile('com.salesforce.marketingcloud:marketingcloudsdk:5.3.+') { 71 | exclude group: 'com.google.android.gms', module: 'play-services-base' 72 | } 73 | ``` 74 | 75 | - Force the version of `com.google.android.gms:play-services-base` to be the lowest one, i.e. 11.0.1: 76 | ``` 77 | compile ("com.google.android.gms:play-services-base:11.0.1") { 78 | force = true; 79 | } 80 | ``` 81 | 82 | - **Run `cd android && ./gradlew clean && cd ..`** to update the dependencies 83 | 84 | 85 | > **Check:** your dependency tree should look like this: 86 | 87 | ``` 88 | _debugCompile - ## Internal use, do not manually configure ## 89 | +--- project :react-native-maps 90 | +--- com.salesforce.marketingcloud:marketingcloudsdk:5.3.+ -> 5.3.1 91 | | +--- com.google.android.gms:play-services-gcm:11.0.1 92 | | | \--- com.google.android.gms:play-services-iid:11.0.1 93 | | | +--- com.google.android.gms:play-services-base:11.0.1 (*) 94 | | | \--- com.google.android.gms:play-services-basement:11.0.1 (*) 95 | +--- com.google.android.gms:play-services-maps:11.0.1 96 | | +--- com.google.android.gms:play-services-base:11.0.1 (*) 97 | | \--- com.google.android.gms:play-services-basement:11.0.1 (*) 98 | +--- com.google.android.gms:play-services-base:11.0.1 (*) 99 | ``` 100 | 101 | Note that now `com.google.android.gms:play-services-base`'s version is 11.0.1 102 | 103 | > **Check:** when you launch your build it either passes or error with a different error 104 | 105 | 106 | ### 3. Repeat if you have a different error 107 | 108 | It's possible that the lib you've downgraded relies on a version of another lib that conflicts with another dependency so you will have to repeat this with this new dependency until your build passes :) 109 | 110 | It's also possible that the lowest version does not have a method required by the highest version. 111 | In this case you can try a version between the two that works. 112 | It's possible that there is no version that works. 113 | -------------------------------------------------------------------------------- /react-native/features/clean-logout.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Implementing a clean logout 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | ## Checks 6 | 7 | You should remove all persisted and in-memory user content after a logout. 8 | You should logout from every external services silently. 9 | 10 | * If my project uses `react-apollo` 11 | * the cache is cleared with [client.resetStore](https://www.apollographql.com/docs/react/advanced/caching.html#reset-store) 12 | * If my project uses `redux` 13 | * there is a `LOGOUT` or `RESET_CUSTOMER_DATA` action that replaces the user reducers' states with the initial states 14 | * If my project uses React-Native `AsyncStorage` 15 | * the cache is cleared with [AsyncStorage.clear](https://facebook.github.io/react-native/docs/asyncstorage.html#clear), or [AsyncStorage.multiRemove](https://facebook.github.io/react-native/docs/asyncstorage.html#multiremove) if you need to target only some keys. 16 | * Errors should be caught and handled 17 | 18 | ## Good examples 19 | 20 | ```js 21 | // pseudo code 22 | const logout = async () => { 23 | try { 24 | await fetch('ZENDESK_LOGOUT_URL', { method: 'GET' }); 25 | await firebase.auth().signOut(); 26 | await AsyncStorage.clear(); 27 | ReduxStore.dispatch(resetCustomerData()); 28 | apolloClient.resetStore(); 29 | Navigation.navigate('login'); 30 | } catch (e) { 31 | HandleErrorService.handleError(e); 32 | // eventually show an error or retry 33 | } 34 | } 35 | ``` 36 | 37 | ## Bad examples 38 | 39 | ### Example 1 40 | 41 | Without cleaning the user data, you will have state and data inconsistencies with the new user. 42 | 43 | ```js 44 | // pseudo code 45 | const logout = async () => { 46 | Navigation.navigate('login'); 47 | } 48 | ``` 49 | 50 | ### Example 2 51 | 52 | Without the `try catch`, the app will crash if the user has no connection, or if another error happens. 53 | 54 | ```js 55 | // pseudo code 56 | const logout = async () => { 57 | await fetch('ZENDESK_LOGOUT_URL', { method: 'GET' }); 58 | await firebase.auth().signOut(); 59 | await AsyncStorage.clear(); 60 | ReduxStore.dispatch(resetCustomerData()); 61 | apolloClient.resetStore(); 62 | Navigation.navigate('login'); 63 | } 64 | ``` -------------------------------------------------------------------------------- /react-native/features/deep-linking.md: -------------------------------------------------------------------------------- 1 | # [MO] How to implement deep linking with react-navigation 2 | 3 | ## Owner : [Nicolas Ngomai](https://github.com/lechinoix) 4 | 5 | * What is deep linking? 6 | 7 | Deeplinking permits you to register a custom URL Scheme in your phone so that when calling a url as `myapp://mypath?param=value` the phone will open the app with the `mypath?param=value`. 8 | In React Native, you can use the `Linking Module` that handle the external links in your app. 9 | When calling a deeplink in your app, a 'url' event will be dispatch with the url associated. 10 | 11 | ## How to implement? 12 | 13 | - Follow this guide from react navigation: https://reactnavigation.org/docs/en/deep-linking.html 14 | - For complementary information, you can consult Linking documentation : https://facebook.github.io/react-native/docs/linking.html 15 | 16 | ## Troubleshooting 17 | 18 | ### iOS 19 | 20 | If you have Facebook SDK installed, a "application" function will already be implemented in your AppDelegate.m 21 | You will need to refactor the two methods as is: 22 | 23 | ```objectivec 24 | - (BOOL)application:(UIApplication *)application 25 | openURL:(NSURL *)url 26 | options:(NSDictionary *)options { 27 | 28 | if([[FBSDKApplicationDelegate sharedInstance] application:application 29 | openURL:url 30 | sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] 31 | annotation:options[UIApplicationOpenURLOptionsAnnotationKey] 32 | ]){ 33 | return YES; 34 | } 35 | else if([RCTLinkingManager application:application openURL:url options:options]){ 36 | return YES; 37 | } 38 | 39 | return NO; 40 | } 41 | 42 | ``` 43 | 44 | ### Android 45 | 46 | If you already have an intent-filter in your MainApplication, had a different one just beside 47 | 48 | ```xml 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /react-native/features/icomoon.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Add new icons to the application 2 | 3 | ## Owner: [Amaury Liet](http://github.com/AmauryLiet/) 4 | 5 | ## Prerequisites 6 | 7 | - A running React Native app, preferably started with the generator 8 | 9 | 10 | ## Intro 11 | 12 | To add new icons to the app, you need to update 3 files in the project. 13 | 14 | These 3 files are in the `icomoon.zip` archive: but if you only have the `*.svg` file of the icon, you need to generate the icomoon archive first. 15 | 16 | ## Prerequisites 17 | 18 | - Have the icons available (either in an `icomoon` file or a `svg` file) 19 | 20 | 21 | ## Steps 22 | 23 | ### 1. [If necessary] Generate to `icomoon.zip` file: 24 | 25 | 1. Go to [Icomoon.io](https://icomoon.io/app/#/select) 26 | 27 | 2. Click on `+ Import icons` 28 | 29 | 3. Import the `selection.json` file of the icons currently in the app, located in `src/components/Icon/selection.json` 30 | 31 | 4. Add icons on the set you just imported by clicking on the burger (on the right) then `Import to set` 32 | 33 | 5. If wanted, rename the icon by passing in `edit` mode (click on the pencil on top of the screen) then click on the icons to rename 34 | 35 | 6. Select the icons you want to export (by default select all of them) 36 | 37 | 7. On the bottom of the screen, click `Generate font` then `Download` 38 | 39 | 40 | ## 2. Add the icons to the application 41 | 42 | 1. Open the `icomoon.zip` file 43 | 44 | 2. Overwrite the following files: 45 | - `android/app/src/main/assets/fonts/icomoon.ttf` 46 | - `src/components/Icon/selection.json` 47 | - `resources/icons/icomoon.ttf` 48 | 49 | 50 | > :warning: **If you are using code-push, don't forget to hard deploy** 51 | > 52 | > Deploying with CodePush will **not** update the icons of the app 53 | 54 | -------------------------------------------------------------------------------- /react-native/features/lock-device-orientation.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Lock the device screen 2 | 3 | ## Owner: [Kévin Jean](https://github.com/Miniplop) 4 | 5 | ## Prerequisites 6 | - A running React Native app, preferably started with the generator 7 | 8 | ## Steps 9 | 10 | - Follow [this](https://github.com/yamill/react-native-orientation/blob/master/README.md) to install react-native-orientation 11 | 12 | > :warning: Do not forget to rebuild your app 13 | 14 | ### Lock all the app pages 15 | 16 | > :warning: Do not lock the screen rotation on react-native-camera or other native modules which needs screen rotation 17 | 18 | We need to lock all the app pages, to do so import react-native-orientation in our top component: 19 | 20 | `import Orientation from 'react-native-orientation` 21 | 22 | Add those lines to lock the orientation: 23 | 24 | ``` 25 | componentWillMount () { 26 | Orientation.lockToPortrait() 27 | } 28 | ``` 29 | 30 | - Refresh your app, and try to rotate your screen 31 | 32 | - [Star the repo](https://github.com/yamill/react-native-orientation) if it works ;) 33 | -------------------------------------------------------------------------------- /react-native/features/offline-redux.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Implementing offline read and write feature 2 | 3 | ## Owner: [Maxime Sraïki](https://github.com/sraikimaxime) 4 | 5 | ## Control Points 6 | 7 | {% hint style='success' %} 8 | 9 | If you want to make your application work offline, you'll need to check that 10 | 11 | {% endhint %} 12 | 13 | * [ ] Data are persisted accross application restart 14 | * [ ] Your user is aware of its connectivity state 15 | * [ ] When you're offline, you gracefully handle the network interraction for the user 16 | 17 | ## Motivation 18 | 19 | Your application has to be used in a low connectivity context (abroad, far from towns, in the subway, in Darius's house, ...) 20 | 21 | ## Prerequisites 22 | 23 | * [ ] Working with React-Native app 24 | * [ ] Using Redux as an application state manager 25 | 26 | {% hint style='info' %} MobX User ? 27 | 28 | If you're using Mobx, [check this out](./offline-mobx.mo.md) 29 | 30 | {% endhint %} 31 | 32 | ## Steps 33 | 34 | ## Knowing wether or not you're offline (~20min) 35 | 36 | ## Reading data offline (~15m) 37 | 38 | {% hint style='success' %} **Control points** 39 | 40 | * [ ] Persist some data 41 | * [ ] Kill your app 42 | * [ ] Turn it to plane mode 43 | * [ ] Open your app 44 | * [ ] See your data 45 | 46 | {% endhint %} 47 | 48 | Use [`redux-persist`](https://github.com/rt2zz/redux-persist). 49 | 50 | ## Writing data offline (~30m) 51 | 52 | ### With Redux-Offline 53 | 54 | You can use [`redux-offline`](https://github.com/redux-offline/redux-offline), basically it provides you with all the ACTION, COMMIT, ROLLBACK logic that let you implement optimistic or defensive offline design. 55 | 56 | To build something optimistic, do the state change on the ACTION, leave the COMMIT empty and rewind the application state on the ROLLBACK. 57 | 58 | To build something defensive, change to a pending state on the ACTION and do the definitive state modification on the COMMIT only, the ROLLBACK is still a rewind of the application state. 59 | 60 | If you are also implementing sagas, take a look at [this issue](https://github.com/redux-offline/redux-offline/issues/173) 61 | 62 | ### Without Redux Offline 63 | 64 | We also implemented writing feature without redux offline on a project @BAM, let me know if you want to know more ! 65 | 66 | ## Tips & TroubleShoot 67 | 68 | * In order to be able to detect this, you can use the react-native's [`NetInfo.isConnected`](https://facebook.github.io/react-native/docs/netinfo.html#docsNav) function to get the connectivity status your phone thinks it has. 69 | 70 | * There are ways to check user's connectivity by pinging one of your own server routes instead of basing it on the phone self awareness of its connectivity. 71 | 72 | * We did not implement offline calls that would need conflict management (several users able to access the same data at the same time) yet. 73 | -------------------------------------------------------------------------------- /react-native/firebase/assets/example-tickets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/firebase/assets/example-tickets.png -------------------------------------------------------------------------------- /react-native/firebase/assets/firebase-debug-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/firebase/assets/firebase-debug-view.png -------------------------------------------------------------------------------- /react-native/firebase/assets/xcode-firebase-debug-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/firebase/assets/xcode-firebase-debug-setup.png -------------------------------------------------------------------------------- /react-native/firebase/debug-events.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Good practices to debug events in Firebase 2 | 3 | ## Owner: [Thomas Pucci](https://github.com/tpucci) 4 | 5 | ## Prerequisites 6 | 7 | * [React Native Firebase](https://github.com/invertase/react-native-firebase) is installed on your project 8 | * You want to track one event 9 | 10 | ## Scrum 11 | 12 | Validating events tracking with Firebase should be done with two tickets: 13 | 14 | 1. You should have one ticket to send the event to Firebase which your PO will validate with a screenshot. 15 | 2. You should have a second ticket to track your event and its parameters on Firebase and save it (this ticket is dependant of the previous one and **can be treated one day after the first ticket is completed**). 16 | 17 | _Example:_ 18 | 19 | ![Trello tickets](./assets/example-tickets.png) 20 | 21 | ## Steps 22 | 23 | ### Set up your device in debug mode (10min) 24 | 25 | Choose to start with either Android or iOS. Once you've finished with an OS, switch to the other. 26 | 27 | #### iOS 28 | 29 | 1. in Xcode, edit the product's scheme. 30 | 31 | {% hint style='info' %} 32 | 33 | Go in **Product**, **Scheme ▶**, **Edit Scheme... ⌘<** or use the shortcut **⌘<**. 34 | 35 | {% endhint %} 36 | 37 | 2. In the left menu, select **Run**. Select the **Arguments** tab. 38 | 3. In the **Arguments passed on launch** section, click the **+** icon (_Add items_). 39 | 4. Type in `-FIRDebugEnabled` and hit enter. 40 | 5. Close the modal. 41 | 6. Build your application and run it on your simulator or device. 42 | 43 | ![Xcode Firebase debug setup](./assets/xcode-firebase-debug-setup.png) 44 | 45 | {% hint style='success' %} **CHECK** 46 | 47 | 1. In Firebase, in the left menu, go in **Analytics**, **Debug View**. 48 | 2. Select your application in the top menu. 49 | 3. Your debug devices count should print: **APPAREIL DE DÉBOGAGE: 1** 50 | 51 | {% endhint %} 52 | 53 | #### Android 54 | 55 | 1. Run your simulator or connect your device. 56 | 57 | {% hint style='success' %} **CHECK** 58 | 59 | Running the command `adb devices` in your shell should display your device ID. 60 | 61 | ```bash 62 | List of devices attached 63 | emulator-5554 device 64 | ``` 65 | 66 | {% endhint %} 67 | 68 | 2. Run the following command: `adb shell setprop debug.firebase.analytics.app ` 69 | 70 | {% hint style='success' %} **CHECK** 71 | 72 | 1. In Firebase, in the left menu, go in **Analytics**, **Debug View**. 73 | 2. Select your application in the top menu. 74 | 3. Your debug devices count should print: **APPAREIL DE DÉBOGAGE: 1** 75 | 76 | {% endhint %} 77 | 78 | ### Use react-native-firebase API to track events (10min) 79 | 80 | 1. Use `logEvent` method. 81 | 82 | _Exemple:_ 83 | 84 | ```js 85 | import firebase from "react-native-firebase"; 86 | 87 | const firebaseAnalytics = firebase.analytics(); 88 | 89 | export default class Analytics { 90 | static trackEvent(eventName, params) { 91 | firebaseAnalytics.logEvent(eventName, params); 92 | } 93 | } 94 | ``` 95 | 96 | 2. In your simulator or on your device, do the action which rises the event. 97 | 98 | {% hint style='success' %} **CHECK**: 99 | 100 | 1. In Firebase, in the left menu, go in **Analytics**, **Debug View**. 101 | 2. Select your application in the top menu. 102 | 3. You should see your events with their parameters 103 | ![Firebase Analytics DebugView](./assets/firebase-debug-view.png) 104 | 105 | {% endhint %} 106 | 107 | 2. (bis) Do the same for Android and iOS. 108 | 109 | ### Complete your task (10min) 110 | 111 | 3. Take a screenshot of the event(s) **with their parameters** your ticket aims to implement. (To see the parameters received by Firebase, click on one event) 112 | 113 | 4. Commit your work **except all modifications to `.xcodeproj`** 114 | 115 | 5. Create your pull request 116 | 117 | 6. Once merged, attach your screenshot to your first ticket which aims to track the event. 118 | 119 | 7. Deploy your changes, and do the action which rises the event on your Staging Application. 120 | 121 | 8. Put your first ticket to validation and add a due date to the second ticket to tomorrow. 122 | 123 | ### Check that your event is tracked and track your event parameters (1 day waiting + 10min) 124 | 125 | 9. In Firebase, in the left menu, go in **Analytics**, **Events**. 126 | 127 | 10. Check that your event is in the list. If not, either something went wrong in the previous steps either Google has not reported any events yet (or Google is down). 128 | 129 | 11. To track parameter, click on your event then on the 'Add parameters' button. 130 | 131 | ## (Optionnal) remove debug mode (10min) 132 | 133 | #### iOS 134 | 135 | Either stash your changes on `.xcodeproj` or do the following: 136 | 137 | 1. in Xcode, edit the product's scheme. 138 | 139 | {% hint style='info' %} 140 | 141 | Go in **Product**, **Scheme ▶**, **Edit Scheme... ⌘<** or use the shortcut **⌘<**. 142 | 143 | {% endhint %} 144 | 145 | 2. In the left menu, select **Run**. Select the **Arguments** tab. 146 | 3. In the **Arguments passed on launch** section, remove `-FIRDebugEnabled` click the **+** icon (_Add items_). 147 | 4. Type in `-FIRDebugDisabled` and hit enter. 148 | 5. Close the modal. 149 | 6. Build your application and run it on your simulator or device. 150 | 151 | ![Xcode Firebase debug setup](./assets/xcode-firebase-debug-setup.png) 152 | 153 | {% hint style='success' %} **CHECK** 154 | 155 | 1. In Firebase, in the left menu, go in **Analytics**, **Debug View**. 156 | 2. Select your application in the top menu. 157 | 3. Your debug devices count should print: **APPAREIL DE DÉBOGAGE: 0** 158 | 159 | {% endhint %} 160 | 161 | #### Android 162 | 163 | 1. Reload the app in your simulator or your device. 164 | 165 | {% hint style='success' %} **CHECK** 166 | 167 | Running the command `adb devices` in your shell should display your device ID. 168 | 169 | ```bash 170 | List of devices attached 171 | emulator-5554 device 172 | ``` 173 | 174 | {% endhint %} 175 | 176 | 2. Run the following command: `adb shell setprop debug.firebase.analytics.app .none.` 177 | 178 | {% hint style='success' %} **CHECK**: 179 | 180 | 1. In Firebase, in the left menu, go in **Analytics**, **Debug View**. 181 | 2. Select your application in the top menu. 182 | 3. Your debug devices count should print: **APPAREIL DE DÉBOGAGE: 0** 183 | 184 | {% endhint %} 185 | -------------------------------------------------------------------------------- /react-native/package-dependencies/handle-dependencies-with-yarn-override.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Handle dependencies with Yarn override 2 | 3 | ## Owner: [Justine Mignot](https://github.com/justinemignot) 4 | 5 | > **Note**: Please create an [issue](https://github.com/bamlab/dev-standards/issues/new) or even a Pull Request with your feedback, typo correction. 6 | 7 | ## Context 8 | 9 | During this standard, we will go through how to handle versions dependencies of different packages to a same package. 10 | 11 | If some package doesn't use same versions of a package, depending of the order it will be called, it won't be the same version of package used. 12 | We want to ensure that the same version is used everywhere in the app. 13 | 14 | Normally this would require to manually edit `yarn.lock` file, but it is really bad because this file is deleted each time you install a new package or run a yarn command. 15 | 16 | By chance, yarn has a feature override, which allows us to fix those versions dependencies. 17 | 18 | ## Prerequisites 19 | 20 | * Have a react native app installed 21 | * Install `moment-timezone` (version "0.5.13") 22 | * Install `react-native-datepicker` (version "1.6.0") 23 | 24 | ## Steps 25 | 26 | ### Figure out a problem 27 | 28 | * Identify the case: 29 | * you just installed a new package (I just installed `react-native-datepicker`) and there is a regression in the app (in my case `moment.locale()` was not working anymore) 30 | * you're trying to set a parameter of a package (e.g. the default locale of `moment`) but it's not taken into account by another package 31 | * Then open `yarn.lock`: 32 | * look for your newly installed package 33 |  \* look for this package dependencies: 34 | 35 | ``` 36 | react-native-datepicker@^1.6.0: 37 | react-native-datepicker@^1.6.0: 38 | version "1.6.0" 39 | resolved "https://registry.yarnpkg.com/react-native-datepicker/-/react-native-datepicker-1.6.0.tgz#3a40dc9b112023c49d60ba2a0789d440b7e3d97c" 40 | dependencies: 41 | moment "2.x.x" 42 | ``` 43 | 44 | * try to find other packages which depends on another version of `moment` 45 | * search for `moment` in `yarn.lock` 46 | * if another package depends on it but in another version, several lines for `moment` will appear: 47 | 48 | ``` 49 | "moment@2.x.x": 50 | version "2.20.1" 51 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" 52 | 53 | "moment@>= 2.9.0": 54 | version "2.18.1" 55 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" 56 | 57 | moment@^2.19.0: 58 | version "2.19.1" 59 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" 60 | ``` 61 | 62 | * find which other package it is by searching for `moment` in dependencies section of packages: 63 | 64 | ``` 65 | moment-timezone@^0.5.13: 66 | version "0.5.14" 67 | resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.14.tgz#4eb38ff9538b80108ba467a458f3ed4268ccfcb1" 68 | dependencies: 69 |   moment ">= 2.9.0" 70 | ``` 71 | 72 | ### Solve the problem 73 | 74 | * In `package.json` file, add a section 'resolutions' at the same level as the 'dependencies' section. 75 | * Specify to which version of `moment` depend the two packages, choose same version number for both: 76 | 77 | ```json 78 | "dependencies": { 79 | ... 80 | }, 81 | "resolutions": { 82 | "react-native-datepicker/moment": "2.19.1", 83 | "moment-timezone/moment": "2.19.1" 84 | }, 85 | ``` 86 | 87 | * If the package is a dependency of your app, you'll need to set its version in the `package.json`: 88 | 89 | ```json 90 | "dependencies": { 91 | "moment": "2.19.1" 92 | }, 93 | "resolutions": { 94 | "react-native-datepicker/moment": "2.19.1", 95 | "moment-timezone/moment": "2.19.1" 96 | } 97 | ``` 98 | 99 | * Run `yarn install`. 100 | 101 | {% hint style='success' %} **CHECK 1** 102 | 103 | Check your yarn.lock file, `moment` should appears in only one line: 104 | 105 | ```txt 106 | moment@2.19.1, moment@2.x.x, "moment@>= 2.9.0", moment@^2.19.0: 107 | version "2.19.1" 108 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" 109 | ``` 110 | 111 | which means that every package depending on `moment` is resolved to a same version (here 2.19.1). 112 | 113 | {% endhint %} 114 | 115 | {% hint style='success' %} **CHECK 2** 116 | 117 | Your app should works correctly now, regression should be solved. `moment` dependencies are solved. 118 | 119 | {% endhint %} 120 | 121 | ## Troubleshooting 122 | 123 | * Yarn documentation : https://yarnpkg.com/en/docs/selective-version-resolutions 124 | -------------------------------------------------------------------------------- /react-native/react-navigation/unmount-compoenent-on-page-change.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Unmount a component on page change *(~14 min)* 2 | 3 | ## Owner: Nicolas Djambazian 4 | 5 | ## Motivation 6 | 7 | React-navigation doesn't unmount your page when you go on the next one. That allow fast transition when you go back. 8 | 9 | But if you are using a Camera component, a WebView or every component which use a lot of resources and can have side effect if it continue to run background, you need to unmount them when you leave the page. 10 | 11 | For that, you can use the [`getPathAndParamsForState` of a Router](https://reactnavigation.org/docs/routers/api). 12 | 13 | ## Prerequisites *(~5min)* 14 | - The page have a container. 15 | -You must have a main stack navigation exported in `src/Routing.js` 16 | 17 | 18 | ## Steps *(~5min)* 19 | 20 | - Add a props `myFeatureIsActivated` on the component with default value at `true` 21 | - In the render method, display you component only if `myFeatureIsActivated` is at true: 22 | 23 | ```jsx 24 | return ( 25 | 26 | {this.props.cameraIsActivated && } 27 | 28 | ); 29 | 30 | ``` 31 | 32 | - Test is your component still work (nothing should have change) 33 | - Import your main stack navigation 34 | - In the mapDispatchToProps, compare the path given by the getPathAndParamsForState to the path of your page, you should have something like: 35 | 36 | ```jsx 37 | import { RootNavigator } from '../../../Routing'; 38 | 39 | const mapStateToProps = state => ({ 40 | cameraActivated: RootNavigator.router.getPathAndParamsForState(state.navigation).path === 'path/of/the/page/in/navigation/stacks', 41 | }); 42 | ``` 43 | 44 | The path is given by your navigation stacks. In the following example, the path of `SIMCardScan` is `activate/simCardScan` 45 | 46 | ```jsx 47 | const ActivateStack = StackNavigator( 48 | { 49 | welcome: { 50 | screen: Pages.SignUp.RequiredToActivateSim, 51 | }, 52 | simCardScan: { 53 | screen: Pages.SignUp.SIMCardScan, 54 | }, 55 | }, 56 | { 57 | initialRouteName: 'welcome, 58 | } 59 | ); 60 | 61 | export const RootNavigator = StackNavigator( 62 | { 63 | landing: { 64 | screen: LandingPage, 65 | }, 66 | activate: { 67 | screen: ActivateStack, 68 | }, 69 | }, 70 | { 71 | initialRouteName: 'landing', 72 | navigationOptions: { 73 | headerStyle: styles.header, 74 | headerTintColor: theme.headerTextColor, 75 | }, 76 | headerMode: 'screen', 77 | } 78 | ); 79 | ``` 80 | 81 | - Test a last time, your component should disappear at the beginning of the page transition. 82 | 83 | -------------------------------------------------------------------------------- /react-native/react/assets/withUncle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/react/assets/withUncle.png -------------------------------------------------------------------------------- /react-native/react/assets/withoutUncle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/react/assets/withoutUncle.png -------------------------------------------------------------------------------- /react-native/react/binding-functions-in-react-component.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Binding functions in react components 2 | 3 | ## Owner: [Jean Faverie](https://github.com/jfaverie) 4 | 5 | > **Note**: Please create an [issue](https://github.com/bamlab/dev-standards/issues/new) or even a Pull Request with your feedback, typo correction. 6 | 7 | ## Context 8 | 9 | During this standard, we will take an example from the egghead react native tutorial when launching an event with a submit button. 10 | 11 | The tutorial does it the old ES5 way when we should better use the ES6 way (with arrow functions). 12 | 13 | ## Prerequisites (~4 min) 14 | 15 | - Have **react-native** installed (`npm i -g react-native-cli`) (~2 min) 16 | - Have a react-native project (`react-native init 37 | ) 38 | } 39 | } 40 | ``` 41 | 42 | - This is bad in two ways: performance and syntax. 43 | 44 | 1. Performance: the first problem here is we use `bind` at every `onChange` event, which is very costly. What we could do is (still not optimal): 45 | 46 | ```javascript 47 | class Test extends Component { 48 | // ... 49 | handleChange(event) { 50 | this.setState({ 51 | username: event.nativeEvent.text, 52 | }).bind(this); 53 | } 54 | // ... 55 | render() { 56 | return ( 57 | 60 | ) 61 | } 62 | } 63 | ``` 64 | we improve our performance, as we only bind at the creation of the class. This is not ideal though. 65 | 66 | 2. The `bind` function is used to be sure to use the good context (the `this` of the class, not the one of the handleChange function). 67 | 68 | ### Good Examples: 69 | We can improve the syntax by using an arrow function, which has no proper context and uses the context of the class (you can use tis example). 70 | 71 | ```javascript 72 | class Test extends Component { 73 | // ... 74 | handleChange = event => { 75 | this.setState({ 76 | username: event.nativeEvent.text, 77 | }); 78 | }; 79 | // ... 80 | render() { 81 | return ( 82 | 85 | ) 86 | } 87 | } 88 | ``` 89 | 90 | The `this` inside the arrow function `handleChange` now refers to the `Test` class. 91 | -------------------------------------------------------------------------------- /react-native/react/enable-overflow-android.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Enable component overflow on MapView 2 | 3 | ## Owner: [Yassine Chbani](https://github.com/yassinecc) 4 | 5 | ## Motivation 6 | 7 | It is usual to position React components in such a way that every child is rendered on top of its parent. Sometimes you want to do something out of the ordinary and let one component overflow on another that is outside its parent. It turns out that Android is quite picky with this and will not render the overflowing part on MapViews. But fear not, there is a way to get around this! 8 | 9 | ## Prerequisites 10 | 11 | Our setup will include one child view that we want rendered on top of its MapView neighbour: 12 | 13 | ![Without uncle](assets/withoutUncle.png) 14 | 15 | 16 | ## Steps (~10 minutes) 17 | 18 | - We start writing our basic views: 19 | 20 | ```jsx 21 | render () { 22 | return ( 23 | 24 | 25 | 26 | 27 | 28 | // Your code goes here 29 | 30 | 31 | 32 | ) 33 | } 34 | ``` 35 | 36 | - The trick here is to introduce `uncle`, a fourth component which will host the child outside the parent. The uncle will have a negative `marginTop` value so that it overlaps with the previously rendered `neighbour` component: 37 | 38 | ```jsx 39 | const OVERLAP_HEIGHT = 50 40 | 41 | const styles = { 42 | uncle: { 43 | backgroundColor: 'transparent', 44 | marginTop: -OVERLAP_HEIGHT 45 | }, 46 | child: { 47 | marginTop: 0 48 | }, 49 | parent: { 50 | marginTop: OVERLAP_HEIGHT 51 | } 52 | neighbour: { 53 | // Another style 54 | } 55 | } 56 | ``` 57 | 58 | The new layout idea is the following: 59 | 60 | ![With uncle](assets/withUncle.png) 61 | 62 | Notice how the `child` if positioned right at the top of its `uncle`. We didn't want to hide the part of `neighbour` that is at the same level as the top of the `child`, so the background color of the `uncle` is set to transparent. In `parent`'s style, we added `marginTop: OVERLAP_HEIGHT` to preserve the original vertical layout of this component with respect to `neighbour`. 63 | 64 | - The last step is to make sure that the child will be rendered on top of the parent. The easiest way it to call the child just before the end of the `uncle`. That way you don't have to deal with `zIndex` properties: 65 | 66 | ```jsx 67 | render () { 68 | return ( 69 | 70 | 71 | 72 | 73 | 74 | // Parent code 75 | 76 | 77 | // Code of the child 78 | 79 | 80 | 81 | ) 82 | } 83 | ``` 84 | 85 | -------------------------------------------------------------------------------- /react-native/react/get-element-size-or-position-with-onLayout.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Get the size or position of a component with onLayout 2 | 3 | ## Owner: [Julien Nassar, Gaspard Denis](https://github.com/juliennassar) 4 | 5 | ## Control Points 6 | 7 | - Hide your dynamically sized (or positioned) component with a `style={{ opacity: 0 }}` during the first rendering of your component to avoid glitches. 8 | 9 | ## Motivation 10 | 11 | If you need : 12 | - to display a list of views without previous knowlegde of their size but want them to be all the same 13 | - to know the size or position of an element on your screen dynamically (whithout explicitly describing it in the object's style) 14 | 15 | To do this, React offers a onLayout props ([see official docs](https://facebook.github.io/react-native/docs/view.html#onlayout)) in which you can pass a callback that will be executed after the rendering of a component. 16 | 17 | ## Prerequisites 18 | 19 | A React-Native app. We will take here the example of a loading bar like this one : 20 | ![loader](https://user-images.githubusercontent.com/13121639/37297957-56f5184e-261f-11e8-9b8b-22c8de783daa.png) 21 | 22 | Your goal is to calculate the number of green pixels rows you have to display for a given loading status from 0 (0%) to 1 (100%) without previous knowledge of the loader width : 23 | 24 | ```jsx 25 | import React from 'react'; 26 | import { View } from 'react-native'; 27 | 28 | export default class Display extends React.Component { 29 | render() { 30 | 31 | 32 | {/* Some react component to display whatever */} 33 | 34 | 35 | 36 | } 37 | } 38 | ``` 39 | 40 | We do not know the size of `LoadingBar` component but we want to display a 33% progress in the `LoadingBar`. 41 | 42 | ## Steps (~5 minutes) 43 | 44 | First add a function to your `LoadingBar` component to get its width, and pass it in the `onLayout` props of your `LoadingBar` Component, and store it in your component's state : 45 | 46 | ```jsx 47 | import React from 'react'; 48 | import { View } from 'react-native'; 49 | 50 | export default class LoadingBar extends React.Component { 51 | constructor(props) { 52 | super(props) 53 | this.state = { 54 | loaderWidth: 0 55 | } 56 | } 57 | 58 | measureLoadingBar = ({nativeEvent}) => this.setState({ loaderWidth: nativeEvent.layout.width }); 59 | 60 | render() { 61 | 62 | {/* Here you want to display x% of your bar, x being the props 'status' passed by the component above */} 63 | 64 | } 65 | } 66 | ``` 67 | 68 | The `onLayout` will generate an event when the LoadingBar is displayed. You can access any layout info with : 69 | ``` 70 | const {posX, posY, width, height} = event.nativeEvent.layout 71 | ``` 72 | 73 | You can now compute the number of green pixels rows with the width we just got with `onLayout` : 74 | ``` 75 | const numbersOfGreenPixelsRows = this.props.status * this.state.loaderWidth 76 | ``` 77 | -------------------------------------------------------------------------------- /react-native/setup/add-cocoapods.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Add Cocoapods to your project (~10min) 2 | 3 | ## Owner: [Yann Leflour](https://github.com/yleflour) 4 | 5 | ## Prerequisites: 6 | 7 | - Have [bundler](http://bundler.io) installed 8 | - Have a *Gemfile* at the root of your project 9 | 10 | ## Steps: 11 | 12 | ### 1. Add cocoapods (~5min) 13 | 14 | - In your *Gemfile* add the line `cocoapods` 15 | - Run `bundle install` 16 | - Go into the iOS project folder (`cd ios`) 17 | - Initialize pods with `bundle exec pod init` 18 | - Add `pods` to *.gitignore* 19 | - Open the `Podfile` 20 | - Remove every *[name]Tests* target 21 | - (Optional) Remove the *[name]-tvOS* target 22 | - Run `bundle exec pod install` 23 | 24 | > **Checks:** 25 | > - You now have a *projectName.xcworkspace* file in the `ios` folder 26 | > - You can run your project by opening *projectName.xcworkspace* with XCode or `react-native run-ios` 27 | > The *pods* folder shouldn't show up in the git diffs 28 | 29 | ### 2. Add your first Pod (~2min) 30 | 31 | - Open the `Podfile` 32 | - Add `pod '', '~> '` inside the target 33 | - Run `bundle exec pod install` 34 | 35 | > **Checks:** 36 | > - The exposed *.h* file from the pod can be imported in *AppDelegate.m* 37 | > - You can run your project by opening *projectName.xcworkspace* with XCode or `react-native run-ios` 38 | -------------------------------------------------------------------------------- /react-native/setup/assets/firebase_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/setup/assets/firebase_android.png -------------------------------------------------------------------------------- /react-native/setup/assets/ios_steps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/setup/assets/ios_steps.png -------------------------------------------------------------------------------- /react-native/setup/assets/stripe_basic_payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/react-native/setup/assets/stripe_basic_payment.png -------------------------------------------------------------------------------- /react-native/setup/deploy-project-to-production.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup & Deploy Project to Production - Only iOS Testflight 2 | 3 | ## Owner: [Xavier Lefèvre](github.com/xavierlefevre) 4 | 5 | ## Prerequisites 6 | - [ ] [React Native Toolbox](https://github.com/bamlab/generator-rn-toolbox) 7 | - [ ] Your client admin account on Apple Developer 8 | - [ ] Your client admin account on iTunes Connect 9 | 10 | ## Steps 11 | 12 | ### 1. Setup Production Env 13 | > Creates the necessary fastlane and JS environments files 14 | ```bash 15 | yo rn-toolbox:fastlane-env 16 | ``` 17 | 18 | ***Answers*** 19 | - Please confirm the project name: `` => The same as for the staging/hockeyapp setup 20 | - The name for this new environment (lowercase, no space): `production` 21 | - The name of your repository Git branch for the environment just set: `production` => Or master... 22 | - The name of the company which will be publishing this application: `Bam` => Or your client name 23 | - The app name for this environment: `` => The final App name 24 | - The App Id for this environment: `com...production` 25 | - Which platform will you use for deployment?: `AppStore` 26 | - Your git repo for match: `git@github.com:/certificates.git` 27 | - The branch you want to use for match: `` => The same as for the staging/hockeyapp setup, one Apple account means one branch usually 28 | - The developer.apple.com team id for the certificates: `**redacted**` => Can be found in Membership (on the website https://developer.apple.com) 29 | - The itunesconnect.apple.com team name: `**redacted**` => Can be found at the bottom top right 30 | - Your apple id: `**redacted**` => The username of the apple account 31 | - Your keystore password: `` => You can directly accept the generated password from the generator 32 | 33 | ### 2. Test your JS Production environment in your emulator 34 | > To make sure that your app is ready to be pushed in production, not showing or using dev tools and calling the right back-end 35 | 36 | ### 3. Generate a certificate and a provisioning profile with setup 37 | ```bash 38 | bundle exec fastlane ios setup --env=production 39 | ``` 40 | 41 | ### 4. Create the app on iTunes Connect 42 | - My apps > Top Left '+' > New App 43 | - Platform: `iOS` 44 | - Name: `` 45 | - Language: `
` 46 | - Bundle ID: `` 47 | - SKU: `` 48 | - Create! 49 | 50 | ### 5. Launch the build process 51 | > It will sign and build your app and upload it to iTunes Connect. 52 | 53 | ```bash 54 | bundle exec fastlane ios deploy --env=production 55 | ``` 56 | 57 | If there is an error due to icons, [check this doc](https://github.com/bamlab/generator-rn-toolbox/blob/master/generators/assets/README.md#generate-icons) 58 | 59 | ### 6. See your app available on Testflight internal testing 60 | - Wait: processing from Apple (can see the status in iTunes Connect > Your App > Activity) 61 | - Your app should be "green" for iTunes Connect Users: iTunes Connect > Your App > Testflight 62 | - Click: iTunes Connect Users to invite testers > '+' 63 | - Add someone that was already invited to manage this account 64 | 65 | ### 7. iTunes status 66 | This is the flow you will go through once you've uploaded your app: 67 | 68 | ![applereviewstatus](https://user-images.githubusercontent.com/30256638/32058288-e47d61f2-ba69-11e7-87c2-8212ad0d4530.png) 69 | 70 | Here is a website that gives the current waiting time for the review step: http://appreviewtimes.com/. 71 | The longest step is 'Waiting for Review' that can last several days. 72 | 73 | ## ToDo 74 | - Add compliance issues detail after the app arrived on iTunes Connect 75 | - Detail how to add a new user to an iTunes Connect account 76 | - Detail how to use match with an [already existing certificate](http://macoscope.com/blog/simplify-your-life-with-fastlane-match/#migration) 77 | -------------------------------------------------------------------------------- /react-native/setup/deploy-script.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Add a deploy script to your app *(~20min)* 2 | 3 | ## Owner: [Xavier Lefèvre](https://github.com/xavierlefevre) 4 | 5 | ## Description 6 | - With one command, deploy with codepush or fastlane, android and/or ios, on the specified environment! 7 | 8 | ## Steps 9 | - Copy paste [deploy.sh](/react-native/setup/deploy.sh) to your project 10 | - Add a deploy command to your `package.json`: 11 | ```json 12 | { 13 | "scripts": { 14 | "deploy": ".//deploy.sh", 15 | ... 16 | } 17 | } 18 | ``` 19 | - Adapt the script to your fastlane and codepush implementation 20 | - Deploy (depending on your implementation): 21 | ```bash 22 | yarn deploy -- -t -o -e 23 | ``` 24 | -------------------------------------------------------------------------------- /react-native/setup/deploy-to-production-android.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Deploy Project to Production on the Google Play Store 2 | 3 | ## Owner: [Yassine Chbani](github.com/yassinecc) 4 | 5 | ## Prerequisites 6 | 7 | * [ ] Your client admin account on Google Play Console 8 | * [ ] You have tested your app's critical features in its production environment 9 | 10 | ## Steps 11 | 12 | ### 1. Bump version 13 | 14 | Bump version code and version name in `fastlane/.env.prod`. 15 | 16 | ### 2. Launch the build process 17 | 18 | This will build your app and upload it to the Google Play Console. 19 | 20 | ```bash 21 | bundle exec fastlane android deploy --env=production 22 | ``` 23 | 24 | ### 3. Make your app available for beta testing 25 | 26 | * Log in to [the developer console](https://play.google.com/apps/publish/) with your project's account and select your app 27 | * Go to `Release Management/App releases` in the left panel 28 | * Click on `Manage Beta` then `Create release` and upload the APK generated from the build process. This file is located at `android/app/build/outputs/apk/app-release.apk` 29 | * Rollout the beta testing and share the download link to your PO 30 | 31 | ⚠️ WARNING ⚠️ Once rolled out into production, it is not possible to directly roll back to APKs that have a lower version code. You'll have to bump the version code and rebuild your app, basically telling the Google Play console you have a new version of the app. 32 | 33 | ## 4. Update the Play Store assets 34 | 35 | If the app's icon or one of its graphics changed, you have to update them separately from the APK: 36 | 37 | * On the left panel, go to `Store presence/Store listing` 38 | * Upload your new graphics then submit your update 39 | 40 | 41 | -------------------------------------------------------------------------------- /react-native/setup/deploy.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | RED='\033[0;31m' 4 | BLUE='\033[0;34m' 5 | CYAN='\033[0;36m' 6 | GREEN='\033[0;32m' 7 | YELLOW='\033[0;33m' 8 | NO_COLOR='\033[0m' 9 | 10 | APP_ENV="staging" 11 | APP_OS="ios and android" 12 | DEPLOY_TYPE="soft" 13 | 14 | while getopts ":e:o:t:" opt; do 15 | case $opt in 16 | e) APP_ENV="$OPTARG" 17 | ;; 18 | o) APP_OS="$OPTARG" 19 | ;; 20 | t) DEPLOY_TYPE="$OPTARG" 21 | ;; 22 | \?) echo "${RED}Invalid option -$OPTARG${NO_COLOR}" >&2 23 | ;; 24 | esac 25 | done 26 | 27 | if [ $DEPLOY_TYPE == "hard" ]; then 28 | echo "${BLUE}* * * * *" 29 | echo "👷 Hard-Deploy" 30 | echo "* * * * *${NO_COLOR}" 31 | if [[ $APP_OS != "android" ]]; then 32 | echo "${GREEN}- - - - -" 33 | echo "Fastlane 🍎 iOS $APP_ENV" 34 | echo "- - - - -${NO_COLOR}" 35 | bundle exec fastlane ios deploy --env=$APP_ENV 36 | fi 37 | if [[ $APP_OS != "ios" ]]; then 38 | echo "${YELLOW}- - - - -" 39 | echo "Fastlane 🤖 Android $APP_ENV" 40 | echo "- - - - -${NO_COLOR}" 41 | bundle exec fastlane android deploy --env=$APP_ENV 42 | fi 43 | fi 44 | 45 | if [ $DEPLOY_TYPE == "soft" ]; then 46 | echo "${CYAN}* * * * *" 47 | echo "🍦 Soft-Deploy" 48 | echo "* * * * *${NO_COLOR}" 49 | 50 | git stash 51 | git checkout integration 52 | git pull 53 | 54 | source fastlane/.env.$APP_ENV 55 | mv src/environment/index.js src/environment/index.js.bak 56 | cp src/environment/index.$APP_ENV.js src/environment/index.js 57 | 58 | LAST_GIT_COMMIT=$(git log HEAD --pretty=format:"%h : %s" -1) 59 | read -e -p "What's the changelog? (leave empty for \"$LAST_GIT_COMMIT\") " INPUT_CHANGELOG 60 | MESSAGE="${INPUT_CHANGELOG:-$LAST_GIT_COMMIT}" 61 | echo "${CYAN}Deploying Commit : $MESSAGE${NO_COLOR}" 62 | 63 | yarn 64 | 65 | if [ $APP_ENV == "production" ]; then 66 | echo "- - - - -" 67 | echo "️️️⚠️ No Codepush for Production yet" 68 | echo "- - - - -" 69 | else 70 | if [[ $APP_OS != "android" ]]; then 71 | echo "${GREEN}- - - - -" 72 | echo "Codepush 🍎 iOS Staging" 73 | echo "- - - - -${NO_COLOR}" 74 | appcenter codepush release-react -d Staging -a /-iOS -m --target-binary-version $IOS_VERSION --description "$MESSAGE" 75 | fi 76 | if [[ $APP_OS != "ios" ]]; then 77 | echo "${YELLOW}- - - - -" 78 | echo "Codepush 🤖 Android Staging" 79 | echo "- - - - -${NO_COLOR}" 80 | appcenter codepush release-react -d Staging -a /-Android -m --target-binary-version $ANDROID_VERSION_NAME --description "$MESSAGE" 81 | fi 82 | fi 83 | 84 | mv src/environment/index.js.bak src/environment/index.js 85 | 86 | fi 87 | -------------------------------------------------------------------------------- /react-native/setup/overriding-existing-app.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] When the final goal is to override an existing app 2 | 3 | ## Owner: [Alexandre Moureaux](https://github.com/almouro) 4 | 5 | ## Context 6 | 7 | You're starting a version 2 of an existing app from scratch. 8 | Replacing the existing app in the stores might be done later in the project, but there are still things to check beforehand: 9 | 10 | ## Checks 11 | 12 | ### - [ ] Check the `targetSdkVersion` of the existing version 13 | 14 | You cannot downgrade the `targetSdkVersion`, so your new project needs to have at least the same `targetSdkVersion` as the old one. 15 | 16 | :warning: **N.B.:** By default, a React Native project has `22` as its `targetSdkVersion`. 17 | 18 | `targetSdkVersion` 23 is for Android 6+ where [permissions are now asked at runtime](http://developer.android.com/training/permissions/requesting.html) and not at installation. 19 | 20 | #### Good examples 21 | 22 | So if the app you're replacing has set 23 or above as the `targetSdkVersion`, your new app needs to have at least that, and should implement permissions at runtime. 23 | 24 | You can use [react-native-permissions](https://github.com/yonahforst/react-native-permissions) to that effect. 25 | 26 | #### Bad Examples (Real story) 27 | 28 | On a project, I had not checked that. Last day on the project, we finally were overriding the existing app,but upon publishing in the Play Store, we couldn't because we were downgrading the `targetSdkVersion`. 29 | 30 | We had to spend more time on the project to use `react-native-permissions` and ask for permissions at runtime. This time would have been saved, had we checked this beforehand. 31 | 32 | ### - [ ] Use the existing app keystore 33 | 34 | Use the previous app's keystore for production builds, even if you're not replacing the app just yet. 35 | 36 | #### Bad examples (Real story) 37 | 38 | On a project, I had not used the production keystore, before the last day. The previous app's keystore had been created with an old java version and we weren't able to sign the app with it. 39 | 40 | We had to spend more time on the project to solve this issue (by updating Java). 41 | -------------------------------------------------------------------------------- /react-native/setup/patch-react-native-android.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Patch React Native for Android (~60min) 2 | 3 | ## Owner: [Louis Zawadzki](https://github.com/louiszawadzki) 4 | 5 | ## Prerequisites: 6 | 7 | - A React Native application 8 | 9 | ## Steps: 10 | 11 | ### 1. Start downloading the Android NDK (2 minutes) 12 | 13 | - Download the NDK [here](https://facebook.github.io/react-native/docs/android-building-from-source.html#download-links-for-android-ndk) (it can take 20 to 30 minutes) 14 | - Once the download has started you can move on to the next steps 15 | 16 | ### 2. Apply your patch at deployment time (15 minutes) 17 | 18 | - Copy the files that contain the fixes inside a `fix-rn` directory at the root of the project 19 | - Create a `scripts/fix-rn.sh` file at the root of your project, looking like: 20 | 21 | ```bash 22 | #!/bin/bash 23 | 24 | cp fix-rn/rn-file.java node_modules/react-native/ReactAndroid/path/to/the/faulty/file.java 25 | cp fix-rn/rn-file2.java node_modules/react-native/ReactAndroid/path/to/the/faulty/file2.java 26 | ``` 27 | 28 | for example: 29 | 30 | ```bash 31 | #!/bin/bash 32 | 33 | cp fix-rn/UIImplementation.java node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java 34 | ``` 35 | 36 | - Make the file executable by running `$ chmod +x scripts/fix-rn.sh` 37 | 38 | > **Checks:** 39 | > 40 | > - when you run `$ scripts/fix-rn.sh` your files have been changed in your node modules 41 | 42 | - Add `scripts/fix-rn.sh` in the `postInstall` of your `package.json` 43 | - Make sure your run `yarn` after you've pulled the latest changes in your deployment script 44 | 45 | > **Checks:** 46 | > 47 | > - when you run your deploy script your files have been changed in your node modules 48 | 49 | ### 3. Configure your app to take the patched version of React Native (5 minutes) 50 | 51 | (This part comes from the [official React Native documentation](https://facebook.github.io/react-native/docs/android-building-from-source.html)) 52 | 53 | #### Adding gradle dependencies 54 | 55 | Add gradle-download-task as dependency in `android/build.gradle`: 56 | 57 | ```groovy 58 | dependencies { 59 | classpath 'com.android.tools.build:gradle:1.3.1' 60 | classpath 'de.undercouch:gradle-download-task:3.1.2' 61 | 62 | // NOTE: Do not place your application dependencies here; they belong 63 | // in the individual module build.gradle files 64 | } 65 | ``` 66 | 67 | #### Adding the :ReactAndroid project 68 | 69 | Add the :ReactAndroid project in `android/settings.gradle`: 70 | 71 | ```groovy 72 | include ':ReactAndroid' 73 | 74 | project(':ReactAndroid').projectDir = new File( 75 | rootProject.projectDir, '../node_modules/react-native/ReactAndroid') 76 | ``` 77 | 78 | Modify your `android/app/build.gradle` to use the :ReactAndroid project instead of the pre-compiled library, e.g. - replace `compile 'com.facebook.react:react-native:+'` with `compile project(':ReactAndroid')`: 79 | 80 | ```groovy 81 | dependencies { 82 | compile fileTree(dir: 'libs', include: ['*.jar']) 83 | compile 'com.android.support:appcompat-v7:23.0.1' 84 | 85 | compile project(':ReactAndroid') 86 | 87 | ... 88 | } 89 | ``` 90 | 91 | #### Making 3rd-party modules use your fork 92 | 93 | If you use 3rd-party React Native modules, you need to override their dependencies so that they don't bundle the pre-compiled library. 94 | Otherwise you'll get an error while compiling - `Error: more than one library with package name 'com.facebook.react'`. 95 | 96 | You don't need to change the code of all your modules. 97 | Modify your `android/app/build.gradle`, and add at the same level that `dependencies` and `android`: 98 | 99 | ```groovy 100 | configurations.all { 101 | exclude group: 'com.facebook.react', module: 'react-native' 102 | } 103 | ``` 104 | 105 | ### 4. Finish NDK installation (5 minutes) 106 | 107 | - Once the NDK has been downloaded unzip it under `/Users/your_unix_name/android-ndk/` (create `android-ndk` if necessary) 108 | - Set `ANDROID_SDK` and `ANDROID_NDK` through you local shell (`.zshrc` or `.bashrc`), for example: 109 | 110 | ```bash 111 | export ANDROID_SDK=/Users/your_unix_name/android-sdk-macosx 112 | export ANDROID_NDK=/Users/your_unix_name/android-ndk/android-ndk-r10e 113 | ``` 114 | 115 | - Don't forget to run `source ~/.zshrc` (or .bashrc) to get the environment variables in your current shell session 116 | 117 | > **Checks:** 118 | > 119 | > - when you run `react-native run-android` your app compiles and you can see that `:ReactAndroid` is built in the logs 120 | -------------------------------------------------------------------------------- /react-native/setup/setup-and-deploy-new-project-to-staging-with-hockeyapp.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup & Deploy New Project to Staging with HockeyApp 2 | 3 | {% hint style='danger' %} **DEPRECATION** 4 | 5 | This standard has been deprecated by [@felixmeziere](https://github.com/felixmeziere) on January 28 in favour of [[MO] Deploy to staging](./setup-and-deploy-new-project-to-staging.mo.md). 6 | 7 | **Reason**: HockeyApp is meant to be deprecated by AppCenter. AppCenter is the new HockeyApp and there is [no loss of functionality](http://blog.m33.network/2017/09/react-native-devops-2-0-overview-of-mobile-center-next-generation-of-hockeyapp/). 8 | 9 | {% endhint %} 10 | 11 | ## Owner: [Yann Leflour](https://github.com/yleflour) 12 | 13 | ## Prerequisites 14 | 15 | * [ ] Have you entire environment setup 16 | * [ ] Generating a new SSH key and adding it to the ssh-agent: (https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/) 17 | 18 | ## Steps 19 | 20 | {% hint style='warning %} **FRIENDLY ADVICE** 21 | 22 | Remember to commit after each step 23 | 24 | {% endhint %} 25 | 26 | ### 1. Setup your React Native App 27 | 28 | ```bash 29 | react-native init 30 | cd 31 | # First Commit 32 | ``` 33 | 34 | ### 2. Setup Fastlane 35 | 36 | ```bash 37 | yo rn-toolbox:fastlane-setup 38 | # Second Commit 39 | ``` 40 | 41 | **_Answers_** 42 | 43 | * Please confirm the project name: `` 44 | * Commit keystore files?: `Y` 45 | * Overwrite : `` 46 | 47 | ### 3. Setup Staging Env 48 | 49 | ```bash 50 | yo rn-toolbox:fastlane-env 51 | # Third Commit 52 | ``` 53 | 54 | **_Answers_** 55 | 56 | * Please confirm the project name: `` 57 | * The name for this new environment (lowercase, no space): `staging` 58 | * The name of your repository Git branch for the environment just set: `` 59 | * The name of the company which will be publishing this application: `Bam` 60 | * The app name for this environment: ` S` 61 | * The App Id for this environment: `tech.bam..staging` 62 | * Which platform will you use for deployment?: `HockeyApp` 63 | * The type of certificate you will be using: `In House (Enterprise only)` 64 | * Your git repo for match: `git@github.com:/certificates.git` 65 | * The branch you want to use for match: `` 66 | * The developer.apple.com team id for the certificates: `**redacted**` 67 | * Your apple id: `**redacted**` 68 | * Your keystore password: `` 69 | * A valid HockeyApp token: `**redacted**` 70 | 71 | ### 4. Deploy Staging 72 | 73 | ```bash 74 | bundle exec fastlane ios deploy --env=staging 75 | bundle exec fastlane android deploy --env=staging 76 | ``` 77 | 78 | ### 5. Create the download link 79 | 80 | * Go to [smarturl.it](https://manage.smarturl.it) 81 | * Go to Hockey App with `**redacted**` 82 | * For each app (Android + iOS) 83 | * Go to `Manage app` 84 | * Go to `Distribution` 85 | * Select `Download Page` > `Public` 86 | * Hit `Save` 87 | * Create a new link 88 | * Default URl: `Trello url` 89 | * Device Destination: 90 | * iPhone: The Hockey App iOS Download & Feedback `Public Page url` 91 | * iPad: The Hockey App iOS Download & Feedback `Public Page url` 92 | * Android: The Hockey App iOS Download & Feedback `Public Page url` 93 | * Organize 94 | * Custom Alias: `smarturl.it/` 95 | 96 | ## Troubleshooting 97 | 98 | If 'Cloning GitHub repo' takes more than 2 minutes: the github servers may be untrusted Trigering a `git clone git@github.com:bamlab/certificates.git` will fix it. 99 | -------------------------------------------------------------------------------- /react-native/setup/setup-and-deploy-new-project-to-staging.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup & Deploy New Project to Staging 2 | 3 | {% hint style='info' %} **NOTE** 4 | 5 | This standard replaces [[MO] Deploy to staging with HockeyApp](setup-and-deploy-new-project-to-staging-with-hockeyapp.mo.md), deprecated by [@felixmeziere](https://github.com/felixmeziere) on January 28. 6 | 7 | **Reason**: HockeyApp is meant to be deprecated by AppCenter. AppCenter is the new HockeyApp and there is [no loss of functionality](http://blog.m33.network/2017/09/react-native-devops-2-0-overview-of-mobile-center-next-generation-of-hockeyapp/). 8 | 9 | {% endhint %} 10 | 11 | ## Owner: [Felix Meziere](https://github.com/felixmeziere) 12 | 13 | ## Prerequisites 14 | 15 | * [ ] Have you entire environment setup 16 | * [ ] Generating a new SSH key and adding it to the ssh-agent: (https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/) 17 | 18 | ## Steps 19 | 20 | {% hint style='warning' %} **FRIENDLY ADVICE** 21 | 22 | Remember to commit after each step 23 | 24 | {% endhint %} 25 | 26 | ### 1. Setup your React Native App 27 | 28 | ```bash 29 | react-native init 30 | cd 31 | # First Commit 32 | ``` 33 | 34 | ### 2. Setup Fastlane 35 | 36 | ```bash 37 | yo rn-toolbox:fastlane-setup 38 | # Second Commit 39 | ``` 40 | 41 | **_Answers_** 42 | 43 | * Please confirm the project name: `` 44 | * Commit keystore files?: `Y` 45 | * Overwrite : `` 46 | 47 | ### 3. Setup Staging Env 48 | 49 | ```bash 50 | yo rn-toolbox:fastlane-env 51 | # Third Commit 52 | ``` 53 | 54 | **_Answers_** 55 | 56 | * Please confirm the project name: `` 57 | * The name for this new environment (lowercase, no space): `staging` 58 | * The name of your repository Git branch for the environment just set: `` 59 | * The name of the company which will be 60 | publishing this application: `Bam` 61 | * Which platform will you use for deployment?: `AppCenter` 62 | * The iOS app name for this environment. Name should be different from the Android app and not contain spaces: `-ios-S` 63 | * The Android app name for this environment. Name should be different from the Android app and not contain spaces: `-android-S` 64 | * The App Id for this environment: `tech.bam..staging` 65 | * The type of certificate you will be using: `In House (Enterprise only)` 66 | * Your git repo for match: `git@github.com:/certificates.git` 67 | * The branch you want to use for match: `` 68 | * The developer.apple.com team id for the certificates: `**redacted**` 69 | * Your apple id: `**redacted**` 70 | * Your keystore password: `` 71 | * A valid App Center API token: `**redacted**` 72 | * A valid App Center Username: `**redacted**` 73 | * (After some npm installation...) Should fastlane modify the Gemfile at path 'xxx' for you? (y/n): `y` 74 | 75 | _Note:_ The AppCenter username is at the bottom-left of the AppCenter interface for a person or is the name of the organization for an organization. 76 | 77 | ### 4. Deployment setup 78 | 79 | ```bash 80 | bundle exec fastlane ios setup --env=staging 81 | # Fourth Commit 82 | ``` 83 | 84 | ### 5. Deploy Staging 85 | 86 | ```bash 87 | bundle exec fastlane ios deploy --env=staging 88 | bundle exec fastlane android deploy --env=staging 89 | ``` 90 | 91 | **_Answers_** 92 | 93 | * OS: `iOS/Android` depending on which you are deploying 94 | * Platform: `React Native` 95 | * Do you want to create a New App?: `yes` 96 | 97 | ### 6. Get the download link 98 | 99 | * For each app (Android + iOS) 100 | * Go to the emails you just got for the two deployments 101 | * Copy the url that the _Install_ button points to and remove the end bit so that it finishes 102 | with `/releases/` 103 | * Go to [smarturl.it](https://manage.smarturl.it) 104 | * Create a new link 105 | * Default URl: `Trello url` 106 | * Device Destination: 107 | * iPhone: The AppCenter iOS Download & Feedback `Public Page url` 108 | * iPad: The AppCenter iOS Download & Feedback `Public Page url` 109 | * Android: The The AppCenter Android Download & Feedback `Public Page url` 110 | * Organize 111 | * Custom Alias: `smarturl.it/` 112 | 113 | ## Troubleshooting 114 | 115 | If 'Cloning GitHub repo' takes more than 2 minutes: the github servers may be untrusted. Triggering a `git clone git@github.com:bamlab/certificates.git` will fix it. 116 | -------------------------------------------------------------------------------- /react-native/setup/setup-facebook-login.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Install Fb React Native Login (v0.47) (~35 min) 2 | 3 | ## Owner: [Guillaume Renouvin](http://github.com/GuillaumeRenouvin/) 4 | 5 | ## Prerequisites (~15 min) 6 | * Have Yarn installed (~2 min) 7 | * Have an application created on Facebook and its id (https://developers.facebook.com/apps) (~10 min) 8 | * Have an account added as developer on your application facebook 9 | * Have cocoapods in your project (~5 min) 10 | 11 | ## Context 12 | When I had to install a facebook login on my React Native project, I thought it would be easy and that I would only have to follow the tutorial of the github repo. But, by simply following the tutorial, I found myself with many problems of versions on both iOS and Android. 13 | 14 | I hope that thanks to this article which summarizes the tips that I could find online, you will be able to install a facebook login in less than an hour. 15 | 16 | ## Steps (~20 min) 17 | Install react-native-fbsdk package (https://github.com/facebook/react-native-fbsdk): `yarn add react-native-fbsdk@0.6.0` (last release 0.6.1 bug on react native link) 18 | 19 | #### iOS (~10 min) 20 | * Link it `react-native link react-native-fbsdk` 21 | * Add in your podfile the library's dependencies: 22 | ``` 23 | target 'accorlocal' do 24 | inherit! :search_paths 25 | pod 'FBSDKCoreKit', '~> 4.25.0' 26 | pod 'FBSDKShareKit', '~> 4.25.0' 27 | pod 'FBSDKLoginKit', '~> 4.25.0' 28 | end 29 | ``` 30 | `cd ios && bundle exec pod install --repo-update` 31 | * Then follow the [documentation](https://developers.facebook.com/quickstarts/?platform=ios) -> Fill your app id -> Configure your info.plist -> connect the App Delegate 32 | 33 | **check:** `console.log(NativeModules)` should show the FacebookLoginManager module on iOS build 34 | 35 | #### Android (~10 min) 36 | * Then follow the [documentation](https://github.com/facebook/react-native-fbsdk#31-android-project) -> "If your react-native version is 0.29 or above" 37 | * Go to [Facebook developer](https://developers.facebook.com/quickstarts/?platform=android) -> Fill your app id -> And follow "Add Facebook App ID" 38 | * There are versions issues on the nodes_modules. To fix them, add a script in /bin/patch-fb-sdk.sh and add it in your package.json as a post install command: 39 | ``` 40 | #!/bin/sh 41 | if [[ $CI ]]; then 42 | sed -i.bak '' 's/@Override//' ./node_modules/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBSDKPackage.java 43 | sed -i.bak '' 's/4\.+/4.22.1/' ./node_modules/react-native-fbsdk/android/build.gradle 44 | else 45 | sed -i.bak 's/@Override//' ./node_modules/react-native-fbsdk/android/src/main/java/com/facebook/reactnative/androidsdk/FBSDKPackage.java 46 | sed -i.bak 's/4\.+/4.22.1/' ./node_modules/react-native-fbsdk/android/build.gradle 47 | fi 48 | ``` 49 | * To debug with android, on you terminal generate a key: `keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64` and add it to [your facebook app](https://developers.facebook.com/apps/1806939376263478/settings/) -> `Key hashings` 50 | 51 | **check 1:** `console.log(NativeModules)` should show the FacebookLoginManager module on Android build 52 | **check 2:** You should be able to log on Facebook on your application on Android on development 53 | 54 | #### Add the Facebook login button 55 | Now, you should be able to add the login button on your app working on both Android and iOS. You just have to import `react-native-fbsdk` and add your Facebook button 56 | ``` 57 | import { TouchableOpacity, Text } from 'react-native'; 58 | import { LoginManager } from 'react-native-fbsdk'; 59 | 60 | class Login extends Component { 61 | tryFacebookLogin() { 62 | // Attempt a login using the Facebook login dialog asking for default permissions. 63 | LoginManager.logInWithReadPermissions(['public_profile', 'email']).then( 64 | function(result) { 65 | if (result.isCancelled) { 66 | alert('Login cancelled'); 67 | } else { 68 | alert('Login success with permissions: ' + result.grantedPermissions.toString()); 69 | } 70 | }, 71 | function(error) { 72 | alert('Login fail with error: ' + error); 73 | } 74 | ); 75 | } 76 | 77 | render() { 78 | return ( 79 | 80 | Facebook button 81 | 82 | ); 83 | } 84 | } 85 | ``` 86 | 87 | You can find more configuration [here](https://github.com/facebook/react-native-fbsdk#login) 88 | -------------------------------------------------------------------------------- /react-native/setup/setup-stripe-dev-standard.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup Stripe in app payment form with tipsi-stripe (~2 hours) 2 | 3 | ## Owner: [Guillaume Renouvin](https://github.com/GuillaumeRenouvin) 4 | 5 | ## Prerequisites 6 | - Have a Stripe account and its publishable/secret key duet that you can find on [your Stripe account](https://dashboard.stripe.com/login) 7 | 8 | ## Context 9 | On a project, I had to let my user pay within my React Native app. My client wanted to use Stripe services. 10 | 11 | First I tried to develop a browser solution with [stripe element](https://stripe.com/docs/stripe-js/elements/quickstart). I had two problems: 12 | - It was really time consuming to develop a web page for the payment; 13 | - It made the user navigate back and forth from the app to their web browser, resulting in a really poor user experience. 14 | 15 | I then found out [tipsi-stripe](https://github.com/tipsi/tipsi-stripe) which is a React Native library that wraps the Stripe native SDK for iOS and Android. It only took me 2 hours to set it up and granted a far better user experience, satisfying our client ;). 16 | 17 | ## However your client should be aware of 18 | - [tipsi-stripe](https://github.com/tipsi/tipsi-stripe) only supports english; 19 | - [tipsi-stripe](https://github.com/tipsi/tipsi-stripe) has not been audited by Stripe yet. The PCI DSS (Payment Card Industry Data Security Standard) regulation committee recently forced Stripe to stop accepting credit card data from direct calls to their API. Therefore, they enforced the usage of "Stripe-controlled" libraries like Elements and their own SDKs. 20 | **It means, Stripe could actually ask you to stop using tipsi in order to fit new PCI DSS requirements anytime.** 21 | So far, tipsi-stripe uses direct calls to Stripe SDKs and therefore, we did not receive any warning from them, still you should keep it in mind. 22 | 23 | However, even if Stripe asked you to stop using tipsi, the time loss would be light as it only takes around 2 hours to install the lib. Setting up browser payment would take more than a day and offers a poorer UX. 24 | 25 | ## Steps (~1 hour) 26 | - Install tipsi-stripe package following its [installation documentation](https://github.com/tipsi/tipsi-stripe#installation) 27 | **check:** 28 | - Break point in iOS in the code; 29 | - Break point in Android in the code; 30 | - `console.log(NativeModules)` (where NativeModules is a react native import) should show the tipsi-stripe module 31 | - I recommend to use the basic add card form to be Stripe friendly 32 | [Documentation](https://github.com/tipsi/tipsi-stripe#request-with-card-form) 33 | 34 | ```js 35 | import React, { PureComponent } from 'react'; 36 | import { View, Text } from 'react-native'; 37 | import stripe from 'tipsi-stripe'; 38 | 39 | const ENV = "staging"; 40 | export default class Payment extends PureComponent { 41 | props: PropsType; 42 | states: StatesType; 43 | 44 | static title = 'Card Form'; 45 | 46 | state = { 47 | token: null, 48 | }; 49 | 50 | componentDidMount() { 51 | stripe.init({ // Place this in the App.js file 52 | publishableKey: "fake token", 53 | androidPayMode: ENV === "production" ? "production" : "test" 54 | }); 55 | } 56 | 57 | handleCardPayPress = async () => { 58 | try { 59 | this.setState({ 60 | token: null, 61 | }); 62 | const token = await stripe.paymentRequestWithCardForm({ 63 | smsAutofillDisabled: true, 64 | }); 65 | 66 | this.setState({ 67 | token, 68 | }); 69 | this.props.addCard(token.tokenId); 70 | } catch (error) {} 71 | }; 72 | 73 | render() { 74 | return ( 75 | 78 | Payment 79 | 80 | ); 81 | } 82 | } 83 | ``` 84 | 85 | ### Plug it with your back 86 | With Stripe, you have a publishable key that you can store on you application and a private key that should never appear in any distributed binary. The publishable key can only be used to create tokens from card informations. Then, you have to combine them with the private key to post it to Stripe through their API. 87 | **Therefore the private key should only be used server side.** 88 | You can find more information [here](https://stripe.com/docs/dashboard#api-keys). 89 | 90 | Here is the most basic example of the relations between your app, your server and Stripe for a payment: 91 | ![Stripe payment example](./assets/stripe_basic_payment.png) 92 | -------------------------------------------------------------------------------- /react-native/setup/setup_firebase_multiple_envs.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Setup Firebase on iOS/Android with multiple environments *(~1h)* 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | Correcter: Thomas Pucci 6 | 7 | ## Prerequisites *(~30min)* 8 | 9 | - [ ] An Android or iOS app with the Firebase SDK installed 10 | 11 | - For React Native apps, you can use [`react-native-firebase`](https://github.com/invertase/react-native-firebase): 12 | - [iOS](https://rnfirebase.io/docs/v3.2.x/installation/ios) 13 | - [Android](https://rnfirebase.io/docs/v3.1.*/installation/android) 14 | - Be careful, when you're using specific modules of firebase, like analytics, you may have to add some `compile` to the `gradle` and some external pods 15 | 16 | 17 | ## Foreword 18 | 19 | You can install and setup Firebase with two different ways: 20 | 21 | | | Javascript asynchronous setup | Native synchronous setup | 22 | |---|---|---| 23 | | Pros | - 👍 No Native code is required
- ⏩ Setup is quick | - 🚀 In your application, you do not need to wait for firebase to instantiate each time you need it
- ✅ You can use Firebase Phone Authentication | 24 | | Cons | ⚠️ You **cannot** use this setup if you aim to use Firebase Phone Authentication as it requires the default Firebase app to be setup (meaning the native synchronous setup to be done: see the paragraph ℹ️ *Important note* [here](https://rnfirebase.io/docs/v3.2.x/core/initialize-apps)) | 📲 You need install native libraries | 25 | 26 | This MO aims to setup Firebase with the **Native synchronous setup**. For the **Javascript asynchronous setup** read the [docs here](https://rnfirebase.io/docs/v3.2.x/core/initialize-apps); and each time you need to call Firebase in your JS code, wait for the Firebase app initialization with `onReady()` method. 27 | 28 | ## Steps *(~30min)* 29 | 30 | It's pretty straightforward to install Firebase when you have only one environment. 31 | But what if you have multiple environments, such as `dev`, `staging` and `production`? 32 | Here's a step-by-step guide. 33 | 34 | ### Android *(~10min)* 35 | On Android, Firebase groups the different configurations in one file. 36 | 37 | 1. Go to the [Firebase console](https://console.firebase.google.com/) 38 | 2. Select your project 39 | 3. Click "Add an application", choose Android 40 | 4. Fill in the form (giving a name like `Staging`), click save, and close the window 41 | 5. Repeat steps 3 to 4 for all your environments 42 | 5. Download the `google-services.json` file 43 | 1. Select one of the Android applications 44 | 2. Click on "google-services.json" 45 | 6. Put `google-services.json` in your `app` folder (`android/app` in React-Native) 46 | 7. You might want to add `google-services.json` in your `.gitignore` 47 | 48 | ![steps](assets/firebase_android.png) 49 | 50 | ### iOS *(~20min)* 51 | On iOS, there is one Firebase configuration file per environment. 52 | 53 | 1. Go to the [Firebase console](https://console.firebase.google.com/) 54 | 2. Select your project 55 | 3. Click "Add an application", choose iOS 56 | 4. Fill in the form (giving a name like `Staging`), click save 57 | 5. Download the configuration file `GoogleService-Info.plist` and rename it like `GoogleService-Info.{APP_IDENTIFIER}.plist` 58 | * Example: for `tech.bam.myApp.staging`, `GoogleService-Info.tech.bam.myApp.staging.plist` 59 | 6. Repeat steps 3 to 5 for all your environments 60 | 7. Duplicate your configuration file for dev environment two times and rename them like so: 61 | * `GoogleService-Info.plist` 62 | * `GoogleService-Info..plist` 63 | 8. Put all your configuration files in the root of your iOS app folder (`ios` in React-Native) 64 | 9. Make sure that your app identifier is injected in your `.pbxproj`. For example, if you're using Fastlane, add a `update_app_identifier` step like: 65 | ```ruby 66 | update_app_identifier( 67 | xcodeproj: xcodeproj_full_path, 68 | plist_path: plist_full_path, 69 | app_identifier: ENV['APP_IDENTIFIER'] 70 | ) 71 | ``` 72 | If you bootstrapped your project with BAM generator, use the following in the iOS build lane: 73 | ```ruby 74 | update_app_identifier( 75 | xcodeproj: xcodeproj, 76 | plist_path: ENV['IOS_PLIST_PATH'], 77 | app_identifier: ENV['IOS_APP_ID'] 78 | ) 79 | ``` 80 | 10. In XCode, in `Build phases`, add a `Select GoogleService-Info.plist` build step before the `Copy Bundle Resources` step that contains: 81 | ```bash 82 | cp "GoogleService-Info.plist" "GoogleService-Info.plist.bak" 83 | cp "GoogleService-Info."$IOS_APP_ID".plist" "GoogleService-Info.plist" 84 | ``` 85 | 11. In XCode, in `Build phases`, add a `Clean GoogleService-Info.plist` build step after the `Copy Bundle Resources` step that contains: 86 | ```bash 87 | cp "GoogleService-Info.plist.bak" "GoogleService-Info.plist" 88 | rm "GoogleService-Info.plist.bak" 89 | ``` 90 | 12. Make sure that the `GoogleService-Info.plist` is listed in the resources of the `Copy Bundle Resources` step 91 | 13. You might want to add `GoogleService-Info.*.plist` in your `.gitignore` 92 | 93 | ![XCode Setup](assets/ios_steps.png) 94 | 95 | ## Troubleshooting 96 | 97 | You can take a look at the commit that adds crash reporting and analytics in DailyScrum: [8005ce3](https://github.com/Minishlink/DailyScrum/commit/8005ce348cc61e9ad4550392fc08ae8a1bad8033) 98 | -------------------------------------------------------------------------------- /react-native/update/upgrade-react-native.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Upgrade react native 2 | 3 | ## Owner: [Tycho Tatitscheff](https://github.com/tychota) 4 | 5 | ## Prerequisites 6 | 7 | - Be sure that all the react native warnings have been fixed by looking at the native logs in the React Native Debugger console. 8 | - Have [react-native-git-upgrade](https://github.com/facebook/react-native/tree/master/react-native-git-upgrade) installed 9 | 10 | ## Warning 11 | 12 | If you are late by several versions do them one at a time. Upgrading several version at a time (like from 0.42 to 0.46) can create a lot of complication, because React Native changes many javascript and native dependencies that also impact your code and its structure. If you do one version at a time, you are sure to be able to more quickly give a working version with a slightly newer version (like from 0.42 to 0.43, etc.) that you tested on both Android and iOS. 13 | 14 | 15 | ## Steps (~12min) 16 | 17 | ### Create a new branch (~1 min) 18 | 19 | - Stash the existing modifications by running `git stash` 20 | - Create a new branch by running `git checkout -b upgrade/react-native-0-XX-0` 21 | 22 | > **Checks**: when running `git status`, you have no untracked or no modified files 23 | 24 | ### Look at the breaking changes of your new release 25 | 26 | - Look at your actual version of React Native 27 | 28 | > Lets say it is `0.45.1` 29 | 30 | - Find the changelog here: https://github.com/facebook/react-native/releases/tag/v0.X.0 where X is the minor of your react native version, note down the major breaking changes you'll have to consider later. 31 | 32 | > **Check**: I have a clear list of deprecated items to check. 33 | 34 | ### Launch the automatic upgrade (~10 mins) 35 | 36 | - Look at this table: https://github.com/ncuillery/rn-diff 37 | 38 | > You want to upgrade one version at the time of react native, but include the patches that are bug fixes for the new version 39 | - In this case, you want to go to `0.46.4` and not `0.46.0` since for the version `0.46`, react-native releases 4 bug fixes that you would have to fix yourself later if you had stay to `0.46.0` 40 | 41 | - Run `react-native-git-upgrade 0.46.4` 42 | 43 | - Fix the potential conflict: see https://facebook.github.io/react-native/docs/upgrading.html#4-resolve-the-conflicts 44 | 45 | - If other modules or libraries have to be updated for compatibility reasons, update them following the upgrade native module steps. 46 | 47 | - Once the React Native upgrade is completed, follow the tests steps (simulator and device) to perform one upgrade at a time. 48 | 49 | ### Upgrade native modules (~10 mins per native module) 50 | 51 | > Note: React Native may cause major breaking changes: 52 | - [v0.40.0](https://github.com/facebook/react-native/releases/v0.40.0) 53 | 54 | - If react native cause a breaking changes in native side (you would see that in the changelog): 55 | 56 | - Open your `android/settings.gradle` and list all module imported 57 | - Compare this list to your `podfile` if you use cocoapods, and the linked framework (in Xcode, the library directory that contains both react-native and react-native plugins framework) @TODO @tycho insert photo 58 | - For each package found, look at the github repo to see if it has been updated, or has pending issues that may jeopardize the upgrade process 59 | - Upgrade the module in `package.json` and run `yarn`. 60 | 61 | > There are also some modules that always require an update for a RN upgrade (example: `react-native-svg`) 62 | 63 | > **Check**: you can now build android and iOS. 64 | 65 | ### Upgrade your own code to fit react new syntax (~10 mins) 66 | 67 | - Identify the breaking change by running the app and googling for the issues 68 | - Run [`react-codemod`](https://github.com/reactjs/react-codemod) to fix all the issues on the codebase. 69 | - [Example props-types](https://github.com/reactjs/react-codemod#react-proptypes-to-prop-types): `jscodeshift -t react-codemod/transforms/ReactNative-View-propTypes.js myproject/src` 70 | 71 | ### Upgrade javascript modules 72 | 73 | > **Note**: For javascript, the overall process is similar than for native module: try to run the app and upgrade all the modules that fail. 74 | 75 | #### React-based changes (~10 mins per module) 76 | 77 | > **Example**: PropTypes gets moved to another package. 78 | 79 | - Try to see if the failing module has an existing fix (then upgrade) 80 | - Otherwise, fork it, and apply the same principle you did on your code to your newly created package. 81 | 82 | #### Packager incompatibilities 83 | 84 | > **Example**: `moment.js` 85 | 86 | - Try to look for github issues on Google 87 | - Use another better architected package (see [this amazing package](https://github.com/date-fns/date-fns)) or create your own. 88 | 89 | ### Fix flow 90 | 91 | // @todo: Tycho 92 | 93 | ### Fix tests 94 | 95 | // @todo: Tycho 96 | 97 | ### Deploy and test on device 98 | 99 | - Once you have tested that everything is working fine on both iOS and Android, deploy on device (do not merge your branch) 100 | - Run the command to hard deploy from your branch to hockeyapp 101 | - Test that everything is working fine on devices 102 | - If everything is correct, merge and soft deploy 103 | 104 | ## Troubleshooting 105 | 106 | 107 | If this is taking too long you can create a new React Native project from scratch with `react-native init` and import and re-install all your javascript and native parts (CocoaPods, linking, etc.). 108 | 109 | There can also be cache issues (with yarn or gradle for instance) so if your project doesn't take long to be cloned you can clone a new instance of your project if you're stuck. 110 | -------------------------------------------------------------------------------- /react-native/use_http_links_in_react_native.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Use HTTP links with RN Components/API Calls on iOS 2 | ## Owner: [Julien Nassar](https://github.com/juliennassar) 3 | ## Sponsor: [Alexandre Moureaux](https://github.com/almouro) 4 | 5 | ## Prerequisites (~ 2 minutes) 6 | - [ ] Have a React Native application [using yo generator](https://github.com/bamlab/generator-rn-toolbox) 7 | 8 | ## Context 9 | 10 | Using HTTP links (in image sources for example) will not work on iOS devices/emulators. By default, the HTTP protocol is disabled. API calls and Image sources using HTTP protocol will not work on iOS, and no errors will be displayed. 11 | 12 | ## Steps (~ 2 minutes) 13 | 14 | Using the default configuration, the images in this page will not be displayed: 15 | 16 | ```javascript 17 | import React, { Component } from 'react' 18 | import { View, Text, Image, TouchableOpacity } from 'react-native' 19 | 20 | 21 | export default class Workspace extends Component { 22 | render () { 23 | return ( 24 | 25 | 26 | 31 | 32 | 33 | ) 34 | } 35 | } 36 | ``` 37 | The source uri uses the HTTP protocol 38 | 39 | the result is this: 40 |
41 | 42 |
43 | No image displayed :( 44 |
45 | 46 | ### how to display these images: 47 | 48 | By default iOS forbids calls using http protocol. Normally you should use secured http protocol (https), if you don't have that opportunity, you have to allow the domain using http protocol by modifying the `/ios//info.plist`: 49 | 50 | in the NSExceptionDomains section, add your domain name and permission 51 | 52 | ```xml 53 | NSExceptionDomains 54 | 55 | localhost 56 | 57 | NSExceptionAllowsInsecureHTTPLoads 58 | 59 | 60 | YOUR_DOMAIN.com 61 | 62 | NSExceptionAllowsInsecureHTTPLoads 63 | 64 | 65 | 66 | ``` 67 |
68 | **- [ ] Rebuild your app** 69 | If you reloading your app it will not work, you modified Native code, so you need to rebuild the native part as well 70 | And it works :) 71 |
72 | 73 |
74 | and it works! 75 | -------------------------------------------------------------------------------- /react/component.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Writing a Component 2 | 3 | ## Owner: Nicolas Djambazian 4 | 5 | ## Standards 6 | 7 | **A react component begins than a `// @flow`** 8 | 9 | Why ? Flow typings will be used to check the Props and the State 10 | 11 | 12 | 13 | **A react component should import React with the following syntax** 14 | ``` 15 | import * as React from 'react'; 16 | ``` 17 | Why ? To be able o access to the flow defined properties : https://flow.org/en/docs/react/types/ 18 | 19 | 20 | **The props and the state must be checked by flow** 21 | 22 | Why ? It allows to : 23 | 24 | - Check the props are valid when you use the component 25 | - Be sure to have done all checks for nullable props 26 | - Give a good updated documentation of the props 27 | 28 | 29 | Ex : 30 | ``` 31 | type Props = { 32 | title?: ?string 33 | id: number, 34 | cards: Array<{ 35 | title: string, 36 | }>, 37 | } 38 | type State = { isOpenend: boolean }; 39 | class MyComponent extends React.PureComponent { 40 | // ... 41 | } 42 | ``` 43 | 44 | **When we open a component file, we should see elements in that orders : props and state typings, class definition, style, hoc and then the final export** 45 | 46 | Why ? The first thing you want to know when you open a component is his API. So the typing should be as first. 47 | 48 | Then You want to know how it works and/or what it contains. So the class should be in seconds. 49 | 50 | Styling is the last thing you want to know. 51 | 52 | 53 | **The styling of a component should be in the same file at the end** 54 | 55 | The style of a component is highlty coupled with his implementation. When you want to change the DOM, you often have to change the style and vice-versa. 56 | 57 | If you want to use the same DOM with different styles, add a `style` props on your component. 58 | 59 | If another component need a part of your style, you have several choices : 60 | - Create a third component with that style, used by both of them 61 | - Add this part of the style in a theme 62 | 63 | 64 | **The Component should be exported as `default`** 65 | 66 | To not have to think of the name of the export. 67 | 68 | **The `render` function should come last** 69 | 70 | To immediately know where to look for the `render` function. 71 | 72 | **Instance methods should not be prefixed with `_`** 73 | 74 | It is common practice to add `_` in front of the private methods of your components. 75 | It turns out that *most* component methods are private. It would mean that most of our methods would need a `_`. 76 | 77 | To not forget any `_`, we simply choose to not put any. 78 | 79 | ## Bad Example 80 | 81 | 82 | ```jsx 83 | // No @flow 84 | import React from 'react'; // Bad React import 85 | import { Text, View } from 'react-native'; 86 | 87 | // style in an other file. 88 | import centeredStyle from '../../../style'; 89 | 90 | // Style at the begining of the file 91 | const styles = { 92 | centeredStyle, 93 | text: { 94 | color: '#bbbbbb' 95 | }, 96 | }; 97 | 98 | // No flow Props typing and no default export 99 | export class Page extends React.PureComponent { 100 | render() { 101 | return ( 102 | 103 | {this.props.text} 104 | 105 | ); 106 | } 107 | } 108 | 109 | ``` 110 | 111 | 112 | ## Good Example 113 | ```jsx 114 | // @flow 115 | import * as React from 'react'; 116 | import { Text } from 'react-native'; 117 | import { CenteredPageContent } from '../components'; 118 | 119 | type Props = { 120 | text: string, 121 | } 122 | 123 | class Page extends React.PureComponent { 124 | render() { 125 | return ( 126 | 127 | {this.props.text} 128 | 129 | ); 130 | } 131 | } 132 | 133 | const styles = { 134 | text: { 135 | color: '#bbbbbb' 136 | }, 137 | }; 138 | 139 | export default Page; 140 | ``` 141 | 142 | -------------------------------------------------------------------------------- /react/lifecycle/trigger-action-on-props-update.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Trigger action on props update with componentWillReceiveProps 2 | 3 | ## Owner: Yassine Chbani 4 | 5 | ## Motivation 6 | 7 | If you have ever worked with React, you will have probably used Redux at some point and found it great as your components can 8 | subscribe to any changes to the store state and react accordingly. But what happens if you want to similarly handle the update 9 | of one of your component's props? For example, call a certain method for a given change of the props? 10 | 11 | Well you're in luck, because Facebook provides a set of methods that get called at different points of the lifecycle of a component 1. We are going to look at the `ComponentWillReceiveProps`method in the following article. 12 | 13 | 14 | ## Prerequisites 15 | 16 | Your page has a set of props that can change after it has been mounted and before it gets unmounted. The props change can be 17 | triggered from within or outside the page. 18 | 19 | ## Steps (~10 minutes) 20 | 21 | - Add to your prop a component that gets updated from outside the page e.g. from a Redux store: 22 | 23 | ```jsx 24 | import { Connectivity } from '../../../Connectivity'; 25 | 26 | const mapStateToProps = state => ({ 27 | isConnected: Connectivity.isConnected(state), 28 | }); 29 | ``` 30 | 31 | Let’s also add a component which is rendered at a certain `isTextVisible` condition: 32 | 33 | ```jsx 34 | class MyClass extends Component { 35 | state = { 36 | isConnected: false 37 | isTextVisible: false 38 | } 39 | 40 | render () { 41 | return ( 42 | if( this.isTextVisible ) { 43 | 44 | Connexion successful! 45 | 46 | } 47 | ) 48 | } 49 | } 50 | ``` 51 | 52 | Let’s say you want to have a special behaviour for this Text component, such that it appears for 3 seconds when `isConnected` changes from `false` to `true`. 53 | 54 | ```jsx 55 | componentWillReceiveProps (nextProps) { 56 | if (!this.props.isConnected && nextProps.isConnected) { 57 | this.setState(isActivated, () => this.makeTextDisappear()) 58 | } 59 | } 60 | 61 | makeTextDisappear = () => { 62 | setTimeout(() => this.setState({isTextVisible: false}), 3000) 63 | } 64 | ``` 65 | 66 | The critical point here is the `if (!this.props.isConnected && nextProps.isConnected)` condition (also called hook). It is necessary to be specific about what prop change triggers the desired action, because `componentWillReceiveProps` is called at every prop update and the code inside is executed each time then. Our code now looks like this: 67 | 68 | ```jsx 69 | import { Connectivity } from '../../../Connectivity'; 70 | 71 | class MyClass extends Component { 72 | state = { 73 | isConnected: false 74 | isTextVisible: false 75 | } 76 | 77 | componentWillReceiveProps (nextProps) { 78 | if (!this.props.isConnected && nextProps.isConnected) { 79 | this.setState(isActivated, () => this.makeTextDisappear()) 80 | } 81 | } 82 | 83 | makeTextDisappear = () => { 84 | setTimeout(() => this.setState({isTextVisible: false}), 3000) 85 | } 86 | 87 | render () { 88 | return ( 89 | if( this.isTextVisible ) { 90 | 91 | Connexion successful! 92 | 93 | } 94 | ) 95 | } 96 | } 97 | 98 | const mapStateToProps = state => ({ 99 | isConnected: Connectivity.isConnected(state), 100 | }); 101 | ``` 102 | 103 | A couple of interesting notes2: 104 | - `componentWillReceiveProps` can be called even if the props did not change 105 | - If done before `render()`is called, then calling `setState` will not trigger an additional render 106 | 107 | 1: See [this](https://engineering.musefind.com/react-lifecycle-methods-how-and-when-to-use-them-2111a1b692b1) or 108 | [that article](https://reactjs.org/docs/react-component.html) about the full lifecycle of a React component. 109 | 110 | 2: https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/component_will_receive_props.html 111 | -------------------------------------------------------------------------------- /react/redux/custom-redux-form-field.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] How to use redux-form with custom fields (~10min) 2 | 3 | ## Owner: [Darius Afchar](https://github.com/DariusAf) 4 | 5 | * What is **redux-form**? 6 | 7 | Redux-form is a higher-order component decorator that enables you to efficiently manage your form by automating a lot of things as form-validation, focus events, storage synchronisation... 8 | 9 | * The point of this article? 10 | 11 | With redux-form, a lot of things are done under the hood, so it may be tricky to add a custom component and have it work as expected. For instance, you might want to use a external component as a date-picker, or a super-component composed with different inputs that should return an aggregated answer. In those cases, you have to get your hands dirty to customise how redux-form connect to your component. Here is one way to do so. 12 | 13 | ## Prerequisities 14 | 15 | A react project with a redux store, obviously. 16 | 17 | * a react project: create one with [create-react-app](https://github.com/facebookincubator/create-react-app) 18 | * [redux installed](http://redux.js.org/docs/basics/UsageWithReact.html), make sure to install **redux** and **react-redux** 19 | * create a store and a reducer 20 | 21 | ## Steps 22 | 23 | 1. Add **redux-form** to your project, run 24 | 25 | ```bash 26 | npm install redux-form 27 | ``` 28 | 29 | 2. Connect redux-form to your store by adding it to your reducers 30 | 31 | It depends on how you did organize your code, but if you have several reducers it might be similar to the following code: 32 | 33 | ```javascript 34 | import { combineReducers } from 'redux'; 35 | import { reducer as formReducer } from 'redux-form'; 36 | // your imports ... 37 | 38 | const appReducer = combineReducers({ 39 | // ... your project reducers 40 | form: formReducer, 41 | }); 42 | 43 | const initialState = {}; 44 | const rootReducer = (state = initialState, action) => (appReducer(state, action)); 45 | 46 | export default rootReducer; 47 | 48 | ``` 49 | 50 | 3. Add a form field component to the front 51 | 52 | Let's say we have a custom component called *MySuperNiceInput* we want to connect. 53 | 54 | ```javascript 55 | import { Field, reduxForm } from 'redux-form' 56 | import { MySuperNiceInput } from 'path/to/MySuperNiceInput' 57 | // your imports ... 58 | 59 | const MyForm = ({ handleSubmit }) => ( 60 |
61 | // ... 62 |
63 | 64 | 65 |
66 |
67 | 68 | 69 |
70 | 71 |
72 | ); 73 | 74 | export default reduxForm( 75 | form: 'myformnamethatwillappearinthestore' 76 | )(MyForm); 77 | ``` 78 | 79 | 4. Control how things change with *this.props.input.onChange()* 80 | 81 | Redux-form automatically pass some props to the component you pass through the *component* prop of *\*. 82 | 83 | ```javascript 84 | import { connect } from 'react-redux'; 85 | // your imports ... 86 | 87 | class MySuperNiceInput extends Component { 88 | handleChange(callbackValue) { 89 | // ... 90 | this.props.input.onChange(valueToSendToStore); 91 | } 92 | 93 | render() { 94 | return ( 95 |
96 | this.handleChange(m)} 99 | /> 100 |
101 | ); 102 | } 103 | } 104 | ``` 105 | 106 | This will dispatch actions to the form reducers whenever onChange is triggered. 107 | 108 | You can find all the props you can pass [on the official documentation](http://redux-form.com/6.0.0-alpha.4/docs/api/Field.md/). 109 | 110 | 6. Manage the submit event 111 | 112 | Time to use what we gave to redux-form. There is a high chance you would like your form data to be sent to a specific store of redux, so that's how to do it. 113 | 114 | Something you have to know about redux-form that can be a bit confusing is that if you want to pass a prop to the form, you have to use the syntax **onNameOfProp** and you receive it as **handleNameOfProp**. 115 | 116 | It means that the wording *handleSubmit* we have used previously is mandatory. We did write 117 | ```const MyForm = ({handleSubmit}) => (
...``` 118 | which is equivalent to 119 | ```const MyForm = props => ( ...``` 120 | 121 | In the light of what was said above, to pass this *handleSubmit* function you will have to use *onSubmit*: 122 | 123 | ```javascript 124 | import React, { Component } from 'react'; 125 | import { connect } from 'react-redux'; 126 | import { reduxSubmitAction } from 'path/to/redux/action'; 127 | import MyForm from 'path/to/my/form'; 128 | 129 | class MyPage extends Component { 130 | submit(data) { 131 | const dataToSubmit = { 132 | firstname: data.firstName, 133 | mycustominput: data.superNiceInput 134 | }; 135 | this.props.dispatchSubmit(dataToSubmit); 136 | } 137 | 138 | render() { 139 | return ( 140 | // ... your page 141 | this.submit(data)} /> 142 | // ... 143 | ); 144 | } 145 | } 146 | 147 | const mapDispatchToProps = (dispatch) => ({ 148 | dispatchSubmit: (data) => { 149 | dispatch(reduxSubmitAction(data)); 150 | } 151 | }); 152 | 153 | const MyPageContainer = connect(null, mapDispatchToProps)(SignUp); 154 | 155 | export default MyPageContainer; 156 | 157 | ``` 158 | 159 | You are done here! 160 | 161 | -------------------------------------------------------------------------------- /react/redux/pass-props-to-container.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Pass props to a container (~5min) 2 | 3 | ## Owner: [Jean Faverie](https://github.com/jfaverie) 4 | 5 | ## Context 6 | 7 | In this article, we will see how to have access to and use the props of a component inside its container. 8 | 9 | ## The point of this article? 10 | 11 | Usually, developers need to have access to the props of a component inside its presentational part (the .component.js file) and not its container. Thus, they use the object form of mapStateToProps and mapDispatchToProps, which don't have access to the props of the component but are easier to use and write. 12 | 13 | ## Prerequisites 14 | 15 | * a react project: create one with [create-react-app](https://github.com/facebookincubator/create-react-app) 16 | * [redux installed](http://redux.js.org/docs/basics/UsageWithReact.html), make sure to install **redux** and **react-redux** 17 | 18 | ## Steps 19 | 20 | 1. Create a simple component's container, for example a Todo component: 21 | 22 | ```javascript 23 | import { connect } from 'react-redux'; 24 | import { selectTodoEvents } from 'redux/todos/selectors'; 25 | import { addTodoEvent } from 'redux/todos/actions'; 26 | import { Todo } from 'Todo.component.js'; 27 | 28 | const mapStateToProps = { 29 | todoEvents: selectTodoEvents, 30 | }; 31 | 32 | const mapDispatchToProps = { 33 | addTodoEvent, 34 | }; 35 | 36 | export default connect(mapStateToProps, mapDispatchToProps)(Todo); 37 | 38 | ``` 39 | 40 | This is a standard container, with a selector giving access to a list of events to the component and an action to create a todo event. 41 | 42 | {% hint style='info' %} 43 | 44 | But what if you want to give a specific behavior to your container? For example enabling the adding of an event only if the todo is editable ? Or seeing only a limited number of events depending on the rights of your user ? You can give props to your container ! Let's see how : 45 | 46 | {% endhint %} 47 | 48 | 2. Refactor your container to keep the same behavior but using the function way. 49 | 50 | To make sure you use the function way correctly, refactor your code but don't change the behavior of your code! 51 | 52 | The function way works exactly the same as the object way but gives you access to more features because you have now access to `dispatch` function. 53 | 54 | ```javascript 55 | import { connect } from 'react-redux'; 56 | import { selectTodoEvents } from 'redux/todos/selectors'; 57 | import { addTodoEvent } from 'redux/todos/actions'; 58 | import { Todo } from 'Todo.component.js'; 59 | 60 | const mapStateToProps = (state) => { 61 | todoEvents: selectTodoEvents(state), 62 | }; 63 | 64 | const mapDispatchToProps = (dispatch) => { 65 | addTodoEvent: () => dispatch(addTodoEvent()), 66 | }; 67 | 68 | export default connect(mapStateToProps, mapDispatchToProps)(Todo); 69 | 70 | ``` 71 | 72 | {% hint style='success' %} **CHECK** 73 | 74 | The behavior must be the same! Test that everything works like before. 75 | 76 | (If it works differently, this is not a refactoring). 77 | 78 | {% endhint %} 79 | 80 | 3. Give access to the props of your component to your container 81 | 82 | We want to have access to 2 props, `hasAdminAccess` and `canAddEvent`. 83 | 84 | ```javascript 85 | import { connect } from 'react-redux'; 86 | import { selectLimitedTodoEvents, selectTodoEvents } from 'redux/todos/selectors'; 87 | import { addTodoEvent, makeAddingRequest } from 'redux/todos/actions'; 88 | import { Todo } from 'Todo.component.js'; 89 | 90 | const mapStateToProps = (state, ownProps) => { 91 | todoEvents: ownProps.hasAdminAccess ? selectTodoEvents(state) : selectLimitedTodoEvents(state), 92 | }; 93 | 94 | const mapDispatchToProps = (dispatch, ownProps) => { 95 | addTodoEvent: () => ownProps.canAddEvent ? dispatch(addTodoEvent()) : dispatch(makeAddingRequest()), 96 | }; 97 | 98 | export default connect(mapStateToProps, mapDispatchToProps)(Todo); 99 | 100 | ``` 101 | 102 | Now you can give a specific action and a specific selector to your container depending on the props of your component! 103 | 104 | {% hint style='success' %} **CHECK 1** 105 | 106 | If you give `hasAdminAccess` and `canAddEvent` props to the container set to true, you should have the same behavior than before 107 | 108 | {% endhint %} 109 | 110 | {% hint style='success' %} **CHECK 2** 111 | 112 | If you give `hasAdminAccess` or `canAddEvent` props to the container set to false, your behavior will be different and you will only be able to see a limited number of events and only make an adding request 113 | 114 | {% endhint %} 115 | -------------------------------------------------------------------------------- /scrum/timebox.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Doing a timebox 2 | 3 | ## Owner: Alice Breton 4 | 5 | ## Checks 6 | 7 | ### Purpose of a timebox 8 | It is hard for people to estimate the amount of time they will have to spend on a specific task, but it is much easier to compare the complexity between two tasks. This is why in the scrum method, tickets are estimated according to their complexity: the more difficult they are, the more points they are worth. 9 | 10 | However, to measure the complexity of a ticket the team must know how to develop it. And sometimes they cannot know in advance how to do it because there is uncertainty that needs to be removed. To overcome the impossibility to estimate the complexity of tickets, we can use a timebox. 11 | 12 | ### What is a timebox? 13 | In a timebox the team decides to spend a certain amount of time on the ticket. 14 | At the end of the timebox, there can be several outcomes, here are a few examples: 15 | - Several solutions were found, it is up to the PR (product owner) to decide which one they would like to see in their project. 16 | - The developer decided which is the best solution and had the time to partially or completely code the functionality. 17 | - During the timebox, no solution was found. 18 | 19 | ### At the end of a timebox 20 | **Write a report before moving the ticket to validation**: Without a report, the PO does not know what to validate. There is a risk of validation return. 21 | 22 | ### Bad examples of a report: 23 | 24 | - The functionality is fully developed, you can validate that it works or create a ticket if it does not. 25 | 26 | What is wrong: 27 | - We don't know precisely what is developed 28 | - It is interesting to give more detail on the different possibilities that were considered, and why the dev chose this one in particular. 29 | 30 | ### Good example of a report: 31 | 32 | The ticket: (3) [TIMEBOX - 3H] As an admin, when a customer is on the last page of the report and clicks on SEND, I see their appointment updated on the CRM. 33 | 34 | 35 | --- 36 | Timebox Report: 37 | I have removed the uncertainty on sending the request to the CRM 38 | 39 | What I have done: 40 | 41 | - Creation of a saga `submitReport` in which: 42 | - A PATCH request is sent to the CRM that changes the field `new_report` on the dynamics 365 website 43 | - The customer is redirected to the dashboard 44 | - When the customer clicks on the `SEND` button on the last page of the report, the saga is launched. 45 | 46 | What is left to do: 47 | 48 | - The id of the appointment is stored in the state of the `report` page when the user clicks on the button `write a report` 49 | - Use this id in the request 50 | - Put in the payload of the request the fields that are in the state of the page `report` to update the report in the CRM 51 | 52 | I have added a ticket in the Sprint+1 with a checklist: (2) As a customer, when I click on the `SEND` button my report is added to the CRM 53 | 54 | --- 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /security/2FA.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] 2-Factor Authentication (2FA) *(~15 min)* 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | ## Prerequisites *(~3 min)* 6 | 7 | * A 2FA app on your phone: 8 | * Authy 9 | * LastPass Authenticator 10 | * Google Authenticator 11 | * A safe place where you'll store backup codes 12 | 13 | ## Steps *(~12 min)* 14 | 15 | 1. Enable 2FA on GitHub 16 | * https://github.com/settings/security 17 | 2. Enable 2FA on Google 18 | * https://myaccount.google.com/signinoptions/two-step-verification 19 | 3. Enable 2FA on Trello 20 | * https://trello.com/2fa 21 | -------------------------------------------------------------------------------- /security/import_certificates_match.mo.md: -------------------------------------------------------------------------------- 1 | # [MO] Import certificates into Match *(~30 min)* 2 | 3 | ## Owner: [Louis Lagrange](https://github.com/Minishlink) 4 | 5 | When using Fastlane's [Match](https://github.com/fastlane/fastlane/tree/master/match), you might want to update the certificates 6 | or provisioning profiles manually if you don't have enough rights on the publisher's Apple developer account 7 | (meaning that you're unable to create certificates and provisioning profile by your own or by using match). 8 | 9 | ## Prerequisites *(~10 min)* 10 | 11 | * A `.p12` distribution certificate and its password, say `cert.p12` 12 | * A `.mobileprovision` provisioning profile, say `profile.mobileprovision` 13 | * An Apple developer account that has read access to the publisher's Apple developer account (check by [logging in](https://developer.apple.com/account/)) 14 | * Write access to the Match git repo, say `certificates.git` 15 | * The app's bundle identifier: `BUNDLE_IDENTIFIER` 16 | * A `list_certificates` lane in your `Fastfile`: 17 | 18 | ```ruby 19 | import fastlane_require 'spaceship' 20 | 21 | # ... 22 | 23 | platform :ios do 24 | lane :list_certificates do 25 | Spaceship.login 26 | Spaceship.select_team 27 | 28 | Spaceship.certificate.all.each do |cert| 29 | cert_type = Spaceship::Portal::Certificate::CERTIFICATE_TYPE_IDS[cert.type_display_id].to_s.split("::")[-1] 30 | puts "Cert id: #{cert.id}, name: #{cert.name}, expires: #{cert.expires.strftime("%Y-%m-%d")}, type: #{cert_type}" 31 | end 32 | end 33 | # ... 34 | ``` 35 | 36 | ## Steps *(~20 min)* 37 | 38 | 1. Install the distribution certificate in your `Keychain` (double click) 39 | 2. In your `Keychain`, under "My certificates", export the certificate in the `.cer` format (right click), say `cert.cer` 40 | 3. Find the new distribution certificate's ID 41 | 1. Run `bundle exec fastlane ios list_certificates` with the account that has access to the publisher's Apple developer account 42 | 2. Find the line corresponding to the new distribution certificate and note the certificate ID `CERT_ID` 43 | 4. Extract the private key from `cert.p12`: `openssl pkcs12 -nocerts -nodes -out cert.pem -in cert.p12` 44 | 5. Open `cert.pem` in a text editor and make sure it starts with `-----BEGIN RSA PRIVATE KEY-----`. If it's the case, go directly to **7**. 45 | 6. Convert the private key to a RSA private key: `openssl rsa -in cert.pem -out cert.pem` 46 | 7. Find the password of the Match git repo's branch (`MATCH_PASSWORD`) in your secret files 47 | 8. Cypher the private key with the Match algorithm: `openssl aes-256-cbc -k ${MATCH_PASSWORD} -in cert.pem -out ${CERT_ID}.p12 -a` 48 | 9. Cypher the public key: `openssl aes-256-cbc -k ${MATCH_PASSWORD} -in cert.cer -out ${CERT_ID}.cer -a` 49 | 10. Cypher the provisioning profile: `openssl aes-256-cbc -k ${MATCH_PASSWORD} -in profile.mobileprovision -out AppStore_${BUNDLE_IDENTIFIER}.mobileprovision -a` 50 | 11. Clone the Match git repository. 51 | 12. Checkout the `MATCH_GIT_BRANCH`. 52 | 13. Remove the old certificates and provisioning profiles and add the new ones. 53 | -------------------------------------------------------------------------------- /successful-sprint/coding/bad_example_car1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/successful-sprint/coding/bad_example_car1.png -------------------------------------------------------------------------------- /successful-sprint/coding/bad_example_car2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamlab/dev-standards/f07fee937df828f63daa6d0dffc36ac9c4e78bbc/successful-sprint/coding/bad_example_car2.png -------------------------------------------------------------------------------- /successful-sprint/coding/plan-your-ticket-to-improve-efficency.s.md: -------------------------------------------------------------------------------- 1 | # [Standard] Good practices to resolve a ticket 2 | 3 | ## Owner: Selim BEN AMMAR 4 | 5 | ## Prerequisites 6 | 7 | * I understand well the userStory 8 | * check: I know how my PO can validate it 9 | * After reading the checklist in my ticket, I go through my code to find the files that will be modified. 10 | * For each file, I find the part of the code that will be targeted. 11 | * I create a git branch: git checkout -b feature—ticketNumber-ComponentName-FeatureShortDescription 12 | * I check the quality of the actual code 13 | 14 | ## Steps (from 5 to 20min) 15 | 16 | To make my code session as efficient as possible, I do a plan by decomposing my functionality in independent parts: 17 | For each of those parts I write down: 18 | 1. The name of the Page/Component where I plan to do a code modification 19 | 2. The detail of the modification I will make 20 | 3. The functional test: Actions I will do in the app to make sure that it works and that it didn’t generate any bug or unexpected behavior. 21 | 4. A reminder to run project automatic tests (prettier, flow, jest…) 22 | 5. A reminder to commit the relevant modifications 23 | 6. An estimation of the time I need to finish this part. 24 | 25 | You can use this template while planning: 26 | 27 | | Component file | Modifications | Technical tests | 28 | |:-----------------:|:------------------:|:------------------:| 29 | | File1.component.js | The modification to do | test | 30 | | File2.style.js | The modification to do | test | 31 | 32 | ## Tips: 33 | * Andon (first your architect, second your coach then other developers) as much as possible while constructing your plan. 34 | * When the plan is finished, you HAVE to present it to a more experienced dev in order to challenge it before coding. 35 | 36 | ## Good Examples: 37 | ### Ticket "Portfolio Creation Summary" 38 | Ticket title: (1) As a user, in the page "Portfolio Creation Summary", when I click on the tab "In the Past", I see the graph data. 39 | **Plan**: 40 | - **Part 1** 41 | - **Goal**: Check that the graph data is already available 42 | - **Functional test**: use the debugger to check that this.state.backtest.composition is not an empty array 43 | - **Duration**: 2 minutes 44 | - **Part 2** 45 | - **Goal**: Show the graph data 46 | - **Solution 1**: Use ul and li ----> backtest.composition.map ((data) => li {data} /li) 47 | * After the architecture challenge, I changed the solution. They proposed **Solution 2** ----> 48 | 49 | ```javascript 50 | ()} 53 | /> 54 | ``` 55 | 56 | - **Functional test**: display the Flatlist 57 | - **Automatic test + commit** 58 | - **Duration**: 15 minutes 59 | - **Total Duration**: 20 minutes 60 | 61 | ## Bad Examples: 62 | ### Ticket "General Car State" 63 | Ticket title: (2) As a user, in the page "General Car State", I see the 4 pictures of the car. 64 | 65 | | What I expected | What I get without doing a plan | 66 | |:-----------------:|:------------------:| 67 | |![](bad_example_car1.png) |![](bad_example_car2.png) 68 | 69 | - **What I did** 70 | - Put the 4 pictures in a container view 71 | - Use flex-wrap to have 2 pictures per row 72 | - **Result** 73 | - As the devices in the definition of the Done had a big screen => Photos were displayed correctly 74 | - **Unexpected result** 75 | - The PO tested it on an iphone 5s (small screen) and the result wasn't the same. 76 | - Therefore, I had to take back this ticket to work on it again (2h of rework). 77 | - My coach encouraged me to use flexDirection (2 cars on a row and the 2 rows in the same column) instead of using flex-wrap. 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /templates/mo.md: -------------------------------------------------------------------------------- 1 | # [MO] *(~<Time> min)* 2 | 3 | ## Owner: <YourFullName> 4 | 5 | ## Prerequisites *(~<Time> min)* 6 | 7 | ## Steps *(~<Time> min)* 8 | --------------------------------------------------------------------------------