Skip to content

Commit

Permalink
Merge branch 'main' into fix/19483-close-anchor-popup
Browse files Browse the repository at this point in the history
  • Loading branch information
srikarparsi committed Aug 1, 2023
2 parents aacee62 + 09b29cc commit 782e029
Show file tree
Hide file tree
Showing 338 changed files with 9,259 additions and 7,803 deletions.
16 changes: 16 additions & 0 deletions .github/ISSUE_TEMPLATE/DesignDocIssue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: Design Doc Project Issue Template
about: A template to follow when creating a new issue related to a design doc project
---

# Part of the <Project Name> project
Main issue: <Issue Link>
Doc section: <Doc Link>
Project: <Project Link>

# Feature Description
<!-- Describe the section of the doc that this issue is covering, along with any relevant screenshots -->

# Manual Test Steps

# Automated Tests
8 changes: 8 additions & 0 deletions .github/workflows/createNewVersion.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Create new version

on:
workflow_dispatch:
inputs:
SEMVER_LEVEL:
description: One of {BUILD, PATCH, MINOR, MAJOR}
required: true
default: BUILD
type: string

workflow_call:
inputs:
SEMVER_LEVEL:
Expand Down
3 changes: 2 additions & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import Onyx from 'react-native-onyx';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {PortalProvider} from '@gorhom/portal';
import './fonts.css';
import ComposeProviders from '../src/components/ComposeProviders';
import HTMLEngineProvider from '../src/components/HTMLEngineProvider';
Expand All @@ -14,7 +15,7 @@ Onyx.init({

const decorators = [
(Story) => (
<ComposeProviders components={[OnyxProvider, LocaleContextProvider, HTMLEngineProvider, SafeAreaProvider]}>
<ComposeProviders components={[OnyxProvider, LocaleContextProvider, HTMLEngineProvider, SafeAreaProvider, PortalProvider]}>
<Story />
</ComposeProviders>
),
Expand Down
1 change: 1 addition & 0 deletions .storybook/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = ({config}) => {
'react-native$': '@expensify/react-native-web',
'react-native-web': '@expensify/react-native-web',
'@react-native-community/netinfo': path.resolve(__dirname, '../__mocks__/@react-native-community/netinfo.js'),
'@react-navigation/native': path.resolve(__dirname, '../__mocks__/@react-navigation/native'),
};

// Necessary to overwrite the values in the existing DefinePlugin hardcoded to the Config staging values
Expand Down
8 changes: 8 additions & 0 deletions __mocks__/@react-navigation/native/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {useIsFocused as realUseIsFocused} from '@react-navigation/native';

// We only want this mocked for storybook, not jest
const useIsFocused = process.env.NODE_ENV === 'test' ? realUseIsFocused : () => true;

export * from '@react-navigation/core';
export * from '@react-navigation/native';
export {useIsFocused};
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001034214
versionName "1.3.42-14"
versionCode 1001034804
versionName "1.3.48-4"
}

splits {
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<resources>
<string name="app_name">Expensify Chat</string>
<string name="app_name">New Expensify</string>
</resources>
1 change: 1 addition & 0 deletions assets/animations/ExpensifyLounge.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/animations/Fireworks.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/animations/Hands.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/animations/PreferencesDJ.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/animations/ReviewingBankInfo.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions assets/animations/WorkspacePlanet.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion config/webpack/webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const {merge} = require('webpack-merge');
const {TimeAnalyticsPlugin} = require('time-analytics-webpack-plugin');
const getCommonConfig = require('./webpack.common');

const BASE_PORT = 8080;
const BASE_PORT = 8082;

/**
* Configuration for the local dev server
Expand Down
2 changes: 1 addition & 1 deletion contributingGuides/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ If you've found a vulnerability, please email security@expensify.com with the su
## Payment for Contributions
We hire and pay external contributors via Upwork.com. If you'd like to be paid for contributing or reporting a bug, please create an Upwork account, apply for an available job in [GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22), and finally apply for the job in Upwork once your proposal gets selected in GitHub. If you think your compensation should be increased for a specific job, you can request a reevaluation by commenting in the Github issue where the Upwork job was posted.

Payment for your contributions and bug reports will be made no less than 7 days after the pull request is deployed to production to allow for regression testing. If a regression occurs, payment will be issued 7 days after all regressions are fixed. If you have not received payment after 8 days of the PR being deployed to production and there being no regressions, please email contributors@expensify.com referencing the GH issue and your GH handle.
Payment for your contributions and bug reports will be made no less than 7 days after the pull request is deployed to production to allow for regression testing. If a regression occurs, payment will be issued 7 days after all regressions are fixed (ie: deployed to production). A 50% penalty will be applied to the Contributor and Contributor+ for each regression on an issue. If you have not received payment after 8 days of the PR being deployed to production, and there are no regressions, please email contributors@expensify.com referencing the GH issue and your GH handle.

New contributors are limited to working on one job at a time, however experienced contributors may work on numerous jobs simultaneously.

Expand Down
2 changes: 1 addition & 1 deletion contributingGuides/FORMS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Form inputs will NOT store draft values by default. This is to avoid accidentall

### Validate on Blur, on Change and Submit

Each individual form field that requires validation will have its own validate test defined. When the form field loses focus (blur) we will run that validate test and show feedback. A blur on one field will not cause other fields to validate or show errors unless they have already been blurred.
Each individual form field that requires validation will have its own validate test defined. When the form field loses focus (blur) we will run that validate test and show feedback. A blur on one field will not cause other fields to validate or show errors unless they have already been blurred. To prevent server errors from being cleared inadvertently, we only run validation on blur if any form data has changed since the last validation/submit.

Once a user has “touched” an input, i.e. blurred the input, we will also start validating that input on change when the user goes back to editing it.

Expand Down
142 changes: 142 additions & 0 deletions contributingGuides/PROPTYPES_CONVERSION_TABLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Expensify PropTypes Conversion Table

## Table of Contents

- [Important Considerations](#important-considerations)
- [Don't Rely on `isRequired`](#dont-rely-on-isrequired)
- [PropTypes Conversion Table](#proptypes-conversion-table)
- [Conversion Example](#conversion-example)

## Important Considerations

### Don't Rely on `isRequired`

Regardless of `isRequired` is present or not on props in `PropTypes`, read through the component implementation to check if props without `isRequired` can actually be optional. The use of `isRequired` is not consistent in the current codebase. Just because `isRequired` is not present, it does not necessarily mean that the prop is optional.

One trick is to mark the prop in question with optional modifier `?`. See if the "possibly `undefined`" error is raised by TypeScript. If any error is raised, the implementation assumes the prop not to be optional.

```ts
// Before
const propTypes = {
isVisible: PropTypes.bool.isRequired,
// `confirmText` prop is not marked as required here, theoretically it is optional.
confirmText: PropTypes.string,
};

// After
type Props = {
isVisible: boolean;
// Consider it as required unless you have proof that it is indeed an optional prop.
confirmText: string; // vs. confirmText?: string;
};
```

## PropTypes Conversion Table

| PropTypes | TypeScript | Instructions |
| -------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PropTypes.any` | `T`, `Record<string, unknown>` or `unknown` | Figure out what would be the correct data type and use it.<br><br>If you know that it's a object but isn't possible to determine the internal structure, use `Record<string, unknown>`. |
| `PropTypes.array` or `PropTypes.arrayOf(T)` | `T[]` or `Array<T>` | Convert to `T[]`, where `T` is the data type of the array.<br><br>If `T` isn't a primitive type, create a separate `type` for the object structure of your prop and use it. |
| `PropTypes.bool` | `boolean` | Convert to `boolean`. |
| `PropTypes.func` | `(arg1: Type1, arg2: Type2...) => ReturnType` | Convert to the function signature. |
| `PropTypes.number` | `number` | Convert to `number`. |
| `PropTypes.object`, `PropTypes.shape(T)` or `PropTypes.exact(T)` | `T` | If `T` isn't a primitive type, create a separate `type` for the `T` object structure of your prop and use it.<br><br>If you want an object but it isn't possible to determine the internal structure, use `Record<string, unknown>`. |
| `PropTypes.objectOf(T)` | `Record<string, T>` | Convert to a `Record<string, T>` where `T` is the data type of values stored in the object.<br><br>If `T` isn't a primitive type, create a separate `type` for the object structure and use it. |
| `PropTypes.string` | `string` | Convert to `string`. |
| `PropTypes.node` | `React.ReactNode` | Convert to `React.ReactNode`. `ReactNode` includes `ReactElement` as well as other types such as `strings`, `numbers`, `arrays` of the same, `null`, and `undefined` In other words, anything that can be rendered in React is a `ReactNode`. |
| `PropTypes.element` | `React.ReactElement` | Convert to `React.ReactElement`. |
| `PropTypes.symbol` | `symbol` | Convert to `symbol`. |
| `PropTypes.elementType` | `React.ElementType` | Convert to `React.ElementType`. |
| `PropTypes.instanceOf(T)` | `T` | Convert to `T`. |
| `PropTypes.oneOf([T, U, ...])` or `PropTypes.oneOfType([T, U, ...])` | `T \| U \| ...` | Convert to a union type e.g. `T \| U \| ...`. |

## Conversion Example

```ts
// Before
const propTypes = {
unknownData: PropTypes.any,
anotherUnknownData: PropTypes.any,
indexes: PropTypes.arrayOf(PropTypes.number),
items: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string,
label: PropTypes.string,
})
),
shouldShowIcon: PropTypes.bool,
onChangeText: PropTypes.func,
count: PropTypes.number,
session: PropTypes.shape({
authToken: PropTypes.string,
accountID: PropTypes.number,
}),
errors: PropTypes.objectOf(PropTypes.string),
inputs: PropTypes.objectOf(
PropTypes.shape({
id: PropTypes.string,
label: PropTypes.string,
})
),
label: PropTypes.string,
anchor: PropTypes.node,
footer: PropTypes.element,
uniqSymbol: PropTypes.symbol,
icon: PropTypes.elementType,
date: PropTypes.instanceOf(Date),
size: PropTypes.oneOf(["small", "medium", "large"]),

optionalString: PropTypes.string,
/**
* Note that all props listed above are technically optional because they lack the `isRequired` attribute.
* However, in most cases, props are actually required but the `isRequired` attribute is left out by mistake.
*
* For each prop that appears to be optional, determine whether the component implementation assumes that
* the prop has a value (making it non-optional) or not. Only those props that are truly optional should be
* labeled with a `?` in their type definition.
*/
};

// After
type Item = {
value: string;
label: string;
};

type Session = {
authToken: string;
accountID: number;
};

type Input = {
id: string;
label: string;
};

type Size = "small" | "medium" | "large";

type Props = {
unknownData: string[];

// It's not possible to infer the data as it can be anything because of reasons X, Y and Z.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
anotherUnknownData: unknown;

indexes: number[];
items: Item[];
shouldShowIcon: boolean;
onChangeText: (value: string) => void;
count: number;
session: Session;
errors: Record<string, string>;
inputs: Record<string, Input>;
label: string;
anchor: React.ReactNode;
footer: React.ReactElement;
uniqSymbol: symbol;
icon: React.ElementType;
date: Date;
size: Size;
optionalString?: string;
};
```
Loading

0 comments on commit 782e029

Please sign in to comment.