From 9b3b430e1a9515f9ccb955d0aafc1dda177312cd Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 3 Apr 2023 08:40:49 -1000 Subject: [PATCH 001/191] Add some frequently asked questions --- contributingGuides/STYLE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index 9d94623f0e33..1dd5ee7d92df 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -618,6 +618,24 @@ There are several ways to use and declare refs and we prefer the [callback metho We love React and learning about all the new features that are regularly being added to the API. However, we try to keep our organization's usage of React limited to the most stable set of features that React offers. We do this mainly for **consistency** and so our engineers don't have to spend extra time trying to figure out how everything is working. That said, if you aren't sure if we have adopted something please ask us first. +# React Hooks: Frequently Asked Questions + +## Are Hooks a Replacement for HOCs or Render Props? + +In most cases, a custom hook is a better pattern to use than an HOC or Render Prop. They are easier to create, understand, use and document. However, there might still be a case for a HOC e.g. if you have a component that abstracts some conditional rendering logic. + +## Where should I put my inline functions? I'm getting a lint error related to `jsx-no-bind`... + +If your inline function does not have any dependencies (i.e. props or state that it depends on) it can be removed from the component and moved to the top of the file. If it does have dependencies, then we should wrap it in `useCallback()` and pass the dependencies as an argument. At one time, we questioned whether there is some performance penalty to using `useCallback()` as a pre-optimization and the conclusion was that there is generally a higher potential cost to not using it vs. using it. + +## Is there an equivalent to `componentDidUpdate()` when using hooks? + +The short answer is no. A longer answer is that sometimes we need to check not only that a dependency has changed, but how it has changed in order to run a side effect. For example, a prop had a value of an empty string on a previous render, but now is non-empty. The generally accepted practice is to store the "previous" value in a `ref` so the comparison can be made in a `useEffect()` call. + +## What is the `exhaustive-deps` lint rule? Can I ignore it? + +A `useEffect()` that does not include referenced props or state in its dependency array is [usually a mistake](https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) as often we want effects to re-run when those dependencies change. However, there are some cases where we might actually only want to re-run the effect when only some of those dependencies change. We determined the best practice here should be to allow disabling the “next line” with a comment `//eslint-disable-next-line react-hooks/exhaustive-deps` and an additional comment explanation so the next developer can understand why the rule was not used. + # Onyx Best Practices [Onyx Documentation](https://github.com/expensify/react-native-onyx) From 5f96597c76211344472c73b7b365280feef11bd1 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 5 Apr 2023 13:37:29 -1000 Subject: [PATCH 002/191] Add some notes about useState()` --- contributingGuides/STYLE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index 1dd5ee7d92df..fb7c71cb02f1 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -628,6 +628,10 @@ In most cases, a custom hook is a better pattern to use than an HOC or Render Pr If your inline function does not have any dependencies (i.e. props or state that it depends on) it can be removed from the component and moved to the top of the file. If it does have dependencies, then we should wrap it in `useCallback()` and pass the dependencies as an argument. At one time, we questioned whether there is some performance penalty to using `useCallback()` as a pre-optimization and the conclusion was that there is generally a higher potential cost to not using it vs. using it. +## Why does `useState()` sometimes get initialized with a function? + +React saves the initial state once and ignores it on the next renders. However, if you pass the result of a function to `useState()` or call a function directly e.g. `useState(doExpensiveThings())` it will *still run on every render*. This can hurt performance depending on what work the function is doing. As an optimization, we can pass an initializer function instead of a value e.g. `useState(doExpensiveThings)` or `useState(() => doExpensiveThings())`. + ## Is there an equivalent to `componentDidUpdate()` when using hooks? The short answer is no. A longer answer is that sometimes we need to check not only that a dependency has changed, but how it has changed in order to run a side effect. For example, a prop had a value of an empty string on a previous render, but now is non-empty. The generally accepted practice is to store the "previous" value in a `ref` so the comparison can be made in a `useEffect()` call. From d91a7bfa4c63a07371b77a84e3de862dc6f0f68c Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 25 Apr 2023 07:44:37 -1000 Subject: [PATCH 003/191] Add some notes about useCallback() vs useMemo() --- contributingGuides/STYLE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index fb7c71cb02f1..dac0f031deaa 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -636,6 +636,10 @@ React saves the initial state once and ignores it on the next renders. However, The short answer is no. A longer answer is that sometimes we need to check not only that a dependency has changed, but how it has changed in order to run a side effect. For example, a prop had a value of an empty string on a previous render, but now is non-empty. The generally accepted practice is to store the "previous" value in a `ref` so the comparison can be made in a `useEffect()` call. +## Are `useCallback()` and `useMemo()` basically the same thing? + +No! Is it easy to confuse `useCallback()` with a memoization helper like `_.memoize()` or `useMemo()`. It is really not the same at all. [`useCallback()` will return a cached function _definition_](https://react.dev/reference/react/useCallback) and will not save us any computational cost of running that function. So, if you are wrapping something in a `useCallback()` and then calling it in the render then it is better to use `useMemo()` to cache the actual **result** of calling that function and use it directly in the render. + ## What is the `exhaustive-deps` lint rule? Can I ignore it? A `useEffect()` that does not include referenced props or state in its dependency array is [usually a mistake](https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) as often we want effects to re-run when those dependencies change. However, there are some cases where we might actually only want to re-run the effect when only some of those dependencies change. We determined the best practice here should be to allow disabling the “next line” with a comment `//eslint-disable-next-line react-hooks/exhaustive-deps` and an additional comment explanation so the next developer can understand why the rule was not used. From e220bee62c506618132145fce56e27905410c83e Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 25 Apr 2023 08:49:44 -1000 Subject: [PATCH 004/191] Update some examples and standardize on function syntax --- contributingGuides/STYLE.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index dac0f031deaa..b9ff75ca928f 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -302,20 +302,24 @@ const {firstName, lastName} = state; ... // Bad -const UserInfo = ({name, email}) => ( - - Name: {name} - Email: {email} - -); +function UserInfo({name, email}) { + return ( + + Name: {name} + Email: {email} + + ); +} // Good -const UserInfo = props => ( - - Name: {props.name} - Email: {props.email} - -); +function UserInfo(props) { + return ( + + Name: {props.name} + Email: {props.email} + + ); +} ``` ## Named vs Default Exports in ES6 - When to use what? @@ -563,7 +567,7 @@ When writing a function component you must ALWAYS add a `displayName` property a ```javascript - const Avatar = (props) => {...}; + function Avatar(props) {...}; Avatar.propTypes = propTypes; Avatar.defaultProps = defaultProps; @@ -644,6 +648,10 @@ No! Is it easy to confuse `useCallback()` with a memoization helper like `_.memo A `useEffect()` that does not include referenced props or state in its dependency array is [usually a mistake](https://legacy.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) as often we want effects to re-run when those dependencies change. However, there are some cases where we might actually only want to re-run the effect when only some of those dependencies change. We determined the best practice here should be to allow disabling the “next line” with a comment `//eslint-disable-next-line react-hooks/exhaustive-deps` and an additional comment explanation so the next developer can understand why the rule was not used. +## Should I declare my components with arrow functions (`const`) or the `function` keyword? + +There are pros and cons of each, but ultimately we have standardized on using the `function` keyword to align things more with modern React conventions. There are also some minor cognitive overhead benefits in that you don't need to think about adding and removing brackets when encountering an implicit return. The `function` syntax also has the benefit of being able to be hoisted where arrow functions do not. + # Onyx Best Practices [Onyx Documentation](https://github.com/expensify/react-native-onyx) From 102509a1ca09927b7dcc0659a481afb64a716519 Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Tue, 9 May 2023 12:01:42 +0300 Subject: [PATCH 005/191] Disable attachment carousel scroll when zoomed in --- src/components/AttachmentView.js | 9 ++++++++- src/components/ImageView/index.js | 4 ++++ src/components/ImageView/index.native.js | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentView.js b/src/components/AttachmentView.js index 23e385cb0c23..ddd50ee16ea9 100755 --- a/src/components/AttachmentView.js +++ b/src/components/AttachmentView.js @@ -99,7 +99,14 @@ const AttachmentView = (props) => { // both PDFs and images will appear as images when pasted into the the text field const isImage = Str.isImage(props.source); if (isImage || (props.file && Str.isImage(props.file.name))) { - const children = ; + const children = ( + + ); + return ( props.onPress ? ( diff --git a/src/components/ImageView/index.js b/src/components/ImageView/index.js index b05be361c9e1..38badcdede89 100644 --- a/src/components/ImageView/index.js +++ b/src/components/ImageView/index.js @@ -12,6 +12,10 @@ const propTypes = { /** Whether source url requires authentication */ isAuthTokenRequired: PropTypes.bool, + /** Handles scale changed event in image zoom component. Used on native only */ + // eslint-disable-next-line react/no-unused-prop-types + onScaleChanged: PropTypes.func.isRequired, + /** URL to full-sized image */ url: PropTypes.string.isRequired, ...windowDimensionsPropTypes, diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 212946ce0dae..716c8c8f57ff 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -21,6 +21,9 @@ const propTypes = { /** URL to full-sized image */ url: PropTypes.string.isRequired, + /** Handles scale changed event in image zoom component. Used on native only */ + onScaleChanged: PropTypes.func.isRequired, + /** Function for handle on press */ onPress: PropTypes.func, @@ -198,12 +201,15 @@ class ImageView extends PureComponent { scale: 2, duration: 100, }); + + this.props.onScaleChanged(2); } // We must be either swiping down or double tapping since we are at zoom scale 1 return false; }} onMove={({scale}) => { + this.props.onScaleChanged(scale); this.imageZoomScale = scale; }} > From 06f7f6dab7b434997948e62ae620e8d541d95f51 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 15 May 2023 15:47:22 -0400 Subject: [PATCH 006/191] refactor has function component --- .../PersonalDetails/DateOfBirthPage.js | 143 ++++++++---------- 1 file changed, 61 insertions(+), 82 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index 0a3d892f7981..e629b3a34439 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, {useState, useEffect, useCallback} from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import moment from 'moment'; @@ -33,105 +33,84 @@ const defaultProps = { }, }; -class DateOfBirthPage extends Component { - constructor(props) { - super(props); - - this.validate = this.validate.bind(this); - this.updateDateOfBirth = this.updateDateOfBirth.bind(this); - this.getYearFromRouteParams = this.getYearFromRouteParams.bind(this); - this.minDate = moment().subtract(CONST.DATE_BIRTH.MAX_AGE, 'Y').toDate(); - this.maxDate = moment().subtract(CONST.DATE_BIRTH.MIN_AGE, 'Y').toDate(); - - this.state = { - selectedYear: '', - }; - } - - componentDidMount() { - this.props.navigation.addListener('focus', this.getYearFromRouteParams); - } - - componentWillUnmount() { - this.props.navigation.removeListener('focus', this.getYearFromRouteParams); - } +const DateOfBirthPage = ({translate, route, navigation, privatePersonalDetails}) => { + const [selectedYear, setSelectedYear] = useState(''); + const minDate = moment().subtract(CONST.DATE_BIRTH.MAX_AGE, 'Y').toDate(); + const maxDate = moment().subtract(CONST.DATE_BIRTH.MIN_AGE, 'Y').toDate(); + const updateDateOfBirth = (values) => PersonalDetails.updateDateOfBirth(values.dob); /** * Function to be called to read year from params - necessary to read passed year from the Year picker which is a separate screen * It allows to display selected year in the calendar picker without overwriting this value in Onyx */ - getYearFromRouteParams() { - const {params} = this.props.route; + const getYearFromRouteParams = useCallback(() => { + const {params} = route; if (params && params.year) { - this.setState({selectedYear: params.year}); + setSelectedYear(params.year); } - } + }, [route]); - /** - * Submit form to update user's first and last legal name - * @param {Object} values - * @param {String} values.dob - date of birth - */ - updateDateOfBirth(values) { - PersonalDetails.updateDateOfBirth(values.dob); - } + useEffect(() => { + navigation.addListener('focus', getYearFromRouteParams); + return () => navigation.removeListener('focus', getYearFromRouteParams); + }, [navigation, getYearFromRouteParams]); /** * @param {Object} values * @param {String} values.dob - date of birth * @returns {Object} - An object containing the errors for each inputID */ - validate(values) { - const errors = {}; - const minimumAge = CONST.DATE_BIRTH.MIN_AGE; - const maximumAge = CONST.DATE_BIRTH.MAX_AGE; - - if (!values.dob || !ValidationUtils.isValidDate(values.dob)) { - errors.dob = this.props.translate('common.error.fieldRequired'); - } - const dateError = ValidationUtils.getAgeRequirementError(values.dob, minimumAge, maximumAge); - if (dateError) { - errors.dob = dateError; - } - - return errors; - } - - render() { - const privateDetails = this.props.privatePersonalDetails || {}; - - return ( - - Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS)} - onCloseButtonPress={() => Navigation.dismissModal(true)} + const validate = useCallback( + (values) => { + const errors = {}; + const minimumAge = CONST.DATE_BIRTH.MIN_AGE; + const maximumAge = CONST.DATE_BIRTH.MAX_AGE; + + if (!values.dob || !ValidationUtils.isValidDate(values.dob)) { + errors.dob = translate('common.error.fieldRequired'); + } + const dateError = ValidationUtils.getAgeRequirementError(values.dob, minimumAge, maximumAge); + if (dateError) { + errors.dob = dateError; + } + + return errors; + }, + [translate], + ); + + return ( + + Navigation.navigate(ROUTES.SETTINGS_PERSONAL_DETAILS)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> +
+ - - - -
- ); - } -} + +
+ ); +}; DateOfBirthPage.propTypes = propTypes; DateOfBirthPage.defaultProps = defaultProps; +DateOfBirthPage.displayName = 'DateOfBirthPage'; export default compose( withLocalize, From 857152879d8989870ffaa2f8e083e7fbfc48ba03 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 15 May 2023 16:03:30 -0400 Subject: [PATCH 007/191] simplify updateDateOfBirth callback --- src/libs/actions/PersonalDetails.js | 2 +- src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index c9c5889c914b..a5e2a2165fcb 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -172,7 +172,7 @@ function updateLegalName(legalFirstName, legalLastName) { /** * @param {String} dob - date of birth */ -function updateDateOfBirth(dob) { +function updateDateOfBirth({dob}) { API.write( 'UpdateDateOfBirth', {dob}, diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index e629b3a34439..39a52dc7e220 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -37,7 +37,6 @@ const DateOfBirthPage = ({translate, route, navigation, privatePersonalDetails}) const [selectedYear, setSelectedYear] = useState(''); const minDate = moment().subtract(CONST.DATE_BIRTH.MAX_AGE, 'Y').toDate(); const maxDate = moment().subtract(CONST.DATE_BIRTH.MIN_AGE, 'Y').toDate(); - const updateDateOfBirth = (values) => PersonalDetails.updateDateOfBirth(values.dob); /** * Function to be called to read year from params - necessary to read passed year from the Year picker which is a separate screen @@ -91,7 +90,7 @@ const DateOfBirthPage = ({translate, route, navigation, privatePersonalDetails}) style={[styles.flexGrow1, styles.ph5]} formID={ONYXKEYS.FORMS.DATE_OF_BIRTH_FORM} validate={validate} - onSubmit={updateDateOfBirth} + onSubmit={PersonalDetails.updateDateOfBirth} submitButtonText={translate('common.save')} enabledWhenOffline > From 10fb0236dbe7d3707ae366810a992a2deb851c31 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Mon, 15 May 2023 16:08:05 -0400 Subject: [PATCH 008/191] move callback inside of the effect and update comment --- .../PersonalDetails/DateOfBirthPage.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js index 39a52dc7e220..060e8fcb741f 100644 --- a/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js +++ b/src/pages/settings/Profile/PersonalDetails/DateOfBirthPage.js @@ -39,20 +39,20 @@ const DateOfBirthPage = ({translate, route, navigation, privatePersonalDetails}) const maxDate = moment().subtract(CONST.DATE_BIRTH.MIN_AGE, 'Y').toDate(); /** - * Function to be called to read year from params - necessary to read passed year from the Year picker which is a separate screen - * It allows to display selected year in the calendar picker without overwriting this value in Onyx + * Reads the year from route params. The year should be set on the route when navigating back from the calendar picker + * This lets us pass the selected year without having to overwrite the value in Onyx */ - const getYearFromRouteParams = useCallback(() => { - const {params} = route; - if (params && params.year) { - setSelectedYear(params.year); - } - }, [route]); - useEffect(() => { + const getYearFromRouteParams = () => { + const {params} = route; + if (params && params.year) { + setSelectedYear(params.year); + } + }; + navigation.addListener('focus', getYearFromRouteParams); return () => navigation.removeListener('focus', getYearFromRouteParams); - }, [navigation, getYearFromRouteParams]); + }, [navigation, route]); /** * @param {Object} values From bf1cc3562d3f2c3885ddc55386f50292a8c1178f Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Fri, 19 May 2023 11:29:09 +0100 Subject: [PATCH 009/191] fix: change all 'two factor' to 'two-factor' in translation --- src/languages/en.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 77d5d229a326..dacfd756bd44 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -26,7 +26,7 @@ export default { zoom: 'Zoom', password: 'Password', magicCode: 'Magic code', - twoFactorCode: 'Two factor code', + twoFactorCode: 'Two-factor code', workspaces: 'Workspaces', profile: 'Profile', payments: 'Payments', @@ -186,7 +186,7 @@ export default { requestNewCode: 'You can also', requestNewCodeLink: 'request a new code here', successfulNewCodeRequest: 'Code requested. Please check your device.', - tfaRequiredTitle: 'Two factor authentication\nrequired', + tfaRequiredTitle: 'Two-factor authentication\nrequired', tfaRequiredDescription: 'Please enter the two-factor authentication code\nwhere you are trying to sign in.', }, moneyRequestConfirmationList: { @@ -514,7 +514,7 @@ export default { stepCodes: 'Recovery codes', keepCodesSafe: 'Keep these recovery codes safe!', codesLoseAccess: - 'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two factor authentication will log you out of all other active sessions.', + 'If you lose access to your authenticator app and don’t have these codes, you will lose access to your account. \n\nNote: Setting up two-factor authentication will log you out of all other active sessions.', stepVerify: 'Verify', scanCode: 'Scan the QR code using your', authenticatorApp: 'authenticator app', @@ -529,8 +529,8 @@ export default { }, twoFactorAuthForm: { error: { - pleaseFillTwoFactorAuth: 'Please enter your two factor code', - incorrect2fa: 'Incorrect two factor authentication code. Please try again.', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', + incorrect2fa: 'Incorrect two-factor authentication code. Please try again.', }, }, passwordConfirmationScreen: { @@ -667,20 +667,20 @@ export default { error: { pleaseFillMagicCode: 'Please enter your magic code', incorrectMagicCode: 'Incorrect magic code.', - pleaseFillTwoFactorAuth: 'Please enter your two factor code', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', }, }, passwordForm: { pleaseFillOutAllFields: 'Please fill out all fields', pleaseFillPassword: 'Please enter your password', - pleaseFillTwoFactorAuth: 'Please enter your two factor code', - enterYourTwoFactorAuthenticationCodeToContinue: 'Enter your two factor authentication code to continue', + pleaseFillTwoFactorAuth: 'Please enter your two-factor code', + enterYourTwoFactorAuthenticationCodeToContinue: 'Enter your two-factor authentication code to continue', forgot: 'Forgot?', requiredWhen2FAEnabled: 'Required when 2FA is enabled', error: { incorrectPassword: 'Incorrect password. Please try again.', incorrectLoginOrPassword: 'Incorrect login or password. Please try again.', - incorrect2fa: 'Incorrect two factor authentication code. Please try again.', + incorrect2fa: 'Incorrect two-factor authentication code. Please try again.', twoFactorAuthenticationEnabled: 'You have 2FA enabled on this account. Please sign in using your email or phone number.', invalidLoginOrPassword: 'Invalid login or password. Please try again or reset your password.', unableToResetPassword: From 9a8ec1158a7b347a2ee5a2e15a1ae52cef402435 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Fri, 19 May 2023 11:30:06 +0100 Subject: [PATCH 010/191] fix: make two-factor consistent in code comments as well --- src/pages/ReimbursementAccount/ValidationStep.js | 2 +- src/pages/signin/PasswordForm.js | 2 +- .../signin/ValidateCodeForm/BaseValidateCodeForm.js | 2 +- tests/unit/ValidationUtilsTest.js | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js index ab8010f1b347..7b2a5b0d6df1 100644 --- a/src/pages/ReimbursementAccount/ValidationStep.js +++ b/src/pages/ReimbursementAccount/ValidationStep.js @@ -41,7 +41,7 @@ const propTypes = { /** User's account who is setting up bank account */ account: PropTypes.shape({ - /** If user has Two factor authentication enabled */ + /** If user has two-factor authentication enabled */ requiresTwoFactorAuth: PropTypes.bool, }), }; diff --git a/src/pages/signin/PasswordForm.js b/src/pages/signin/PasswordForm.js index 6bbe1d9585ad..07d988d172af 100755 --- a/src/pages/signin/PasswordForm.js +++ b/src/pages/signin/PasswordForm.js @@ -29,7 +29,7 @@ const propTypes = { /** The details about the account that the user is signing in with */ account: PropTypes.shape({ - /** Whether or not two factor authentication is required */ + /** Whether or not two-factor authentication is required */ requiresTwoFactorAuth: PropTypes.bool, /** Whether or not a sign on form is loading (being submitted) */ diff --git a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js index 04fbe20cca61..952de8d265bf 100755 --- a/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js +++ b/src/pages/signin/ValidateCodeForm/BaseValidateCodeForm.js @@ -31,7 +31,7 @@ const propTypes = { /** The details about the account that the user is signing in with */ account: PropTypes.shape({ - /** Whether or not two factor authentication is required */ + /** Whether or not two-factor authentication is required */ requiresTwoFactorAuth: PropTypes.bool, /** Whether or not a sign on form is loading (being submitted) */ diff --git a/tests/unit/ValidationUtilsTest.js b/tests/unit/ValidationUtilsTest.js index e2d7b13f3364..48ad9f20b028 100644 --- a/tests/unit/ValidationUtilsTest.js +++ b/tests/unit/ValidationUtilsTest.js @@ -2,23 +2,23 @@ const ValidationUtils = require('../../src/libs/ValidationUtils'); describe('ValidationUtils', () => { describe('isValidTwoFactorCode', () => { - test('numeric two factor code', () => { + test('numeric two-factor code', () => { expect(ValidationUtils.isValidTwoFactorCode('123456')).toBe(true); }); - test('numeric two factor code with leading zeroes', () => { + test('numeric two-factor code with leading zeroes', () => { expect(ValidationUtils.isValidTwoFactorCode('000001')).toBe(true); }); - test('alphanumeric two factor code', () => { + test('alphanumeric two-factor code', () => { expect(ValidationUtils.isValidTwoFactorCode('abc123')).toBe(false); }); - test('special characters two factor code', () => { + test('special characters two-factor code', () => { expect(ValidationUtils.isValidTwoFactorCode('!@#$%^')).toBe(false); }); - test('partial special characters two factor code', () => { + test('partial special characters two-factor code', () => { expect(ValidationUtils.isValidTwoFactorCode('123$%^')).toBe(false); }); }); From 6a3ec6677fd187223b12c30507a37fe8ddd275a1 Mon Sep 17 00:00:00 2001 From: Ana Margarida Silva Date: Fri, 19 May 2023 11:32:50 +0100 Subject: [PATCH 011/191] fix: use download translation in CodesPage --- src/pages/settings/Security/TwoFactorAuth/CodesPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js index 915d2534fa0d..827b09d733a8 100644 --- a/src/pages/settings/Security/TwoFactorAuth/CodesPage.js +++ b/src/pages/settings/Security/TwoFactorAuth/CodesPage.js @@ -102,7 +102,7 @@ function CodesPage(props) { style={styles.twoFactorAuthCodesButton} />