-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Room - Sign in RHP disappears after clicking on the '<' button. #46760
fix: Room - Sign in RHP disappears after clicking on the '<' button. #46760
Conversation
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@ishpaul777 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
@ishpaul777, do you know how we can open the public report in the native and desktop apps? |
for desktop, make sure desktop app is running and then on browser visit |
@ishpaul777, can you please share the steps from slack, I'm not in the channel. |
You can use npx uri-scheme open , e.g.
|
Reviewer Checklist
Screenshots/VideosAndroid: NativeScreen.Recording.2024-08-15.at.12.41.51.AM.movAndroid: mWeb ChromeScreen.Recording.2024-08-15.at.12.29.27.AM.moviOS: NativeScreen.Recording.2024-08-13.at.2.03.08.AM.moviOS: mWeb SafariScreen.Recording.2024-08-15.at.12.19.44.AM.movMacOS: Chrome / SafariScreen.Recording.2024-08-13.at.12.27.46.AM.movMacOS: Desktop |
useEffect(() => { | ||
setClearSignInData(clearSignInData); | ||
}, [clearSignInData, setClearSignInData]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using useImperativeHandle
here is more appropriapete, see usage in https://github.com/ishpaul777/App/blob/main/src/pages/home/report/ReportActionCompose/Suggestions.tsx
I will update today, I have to resolve one more issue, the back button has extra top margin on desktop app because we are using |
@ishpaul777, what do you think about remove the |
how about keeping the HeaderWithBackButton in SigninModal as it is now and using a navigateBack function as ref in SigninModal using useImperativeHandle ? |
Sounds good to me. Will update accordingly. |
gentle bump @Krishna2323 |
@ishpaul777, I was trying to update the test the code but for some reason I can't login on desktop app. I will try to fix the issue and push the code changes. Monosnap.screencast.2024-08-09.16-05-33.mp4 |
Can you try clearing onyx data and then try again |
@ishpaul777, thanks, it worked. I'm struggling a bit to implement Codeimport {Str} from 'expensify-common';
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {ForwardedRef} from 'react';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import CustomStatusBarAndBackground from '@components/CustomStatusBarAndBackground';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import ScreenWrapper from '@components/ScreenWrapper';
import ThemeProvider from '@components/ThemeProvider';
import ThemeStylesProvider from '@components/ThemeStylesProvider';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ActiveClientManager from '@libs/ActiveClientManager';
import * as Localize from '@libs/Localize';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import Performance from '@libs/Performance';
import Visibility from '@libs/Visibility';
import * as App from '@userActions/App';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Account, Credentials, Locale} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import ChooseSSOOrMagicCode from './ChooseSSOOrMagicCode';
import EmailDeliveryFailurePage from './EmailDeliveryFailurePage';
import LoginForm from './LoginForm';
import type {InputHandle} from './LoginForm/types';
import SignInPageLayout from './SignInPageLayout';
import type {SignInPageLayoutRef} from './SignInPageLayout/types';
import SignUpWelcomeForm from './SignUpWelcomeForm';
import UnlinkLoginForm from './UnlinkLoginForm';
import ValidateCodeForm from './ValidateCodeForm';
type SignInPageInnerOnyxProps = {
/** The details about the account that the user is signing in with */
account: OnyxEntry<Account>;
/** The credentials of the person signing in */
credentials: OnyxEntry<Credentials>;
/** Active Clients connected to ONYX Database */
activeClients: OnyxEntry<string[]>;
/** The user's preferred locale */
preferredLocale: OnyxEntry<Locale>;
};
type SignInPageInnerProps = SignInPageInnerOnyxProps & {
shouldEnableMaxHeight?: boolean;
shouldShowBackButton?: boolean;
};
type SignInPageRef = {
navigateBack: () => void;
};
type RenderOption = {
shouldShowLoginForm: boolean;
shouldShowEmailDeliveryFailurePage: boolean;
shouldShowUnlinkLoginForm: boolean;
shouldShowValidateCodeForm: boolean;
shouldShowChooseSSOOrMagicCode: boolean;
shouldInitiateSAMLLogin: boolean;
shouldShowWelcomeHeader: boolean;
shouldShowWelcomeText: boolean;
shouldShouldSignUpWelcomeForm: boolean;
};
type GetRenderOptionsParams = {
hasLogin: boolean;
hasValidateCode: boolean;
account: OnyxEntry<Account>;
isPrimaryLogin: boolean;
isUsingMagicCode: boolean;
hasInitiatedSAMLLogin: boolean;
shouldShowAnotherLoginPageOpenedMessage: boolean;
credentials: OnyxEntry<Credentials>;
};
/**
* @param hasLogin
* @param hasValidateCode
* @param account
* @param isPrimaryLogin
* @param isUsingMagicCode
* @param hasInitiatedSAMLLogin
* @param hasEmailDeliveryFailure
*/
function getRenderOptions({
hasLogin,
hasValidateCode,
account,
isPrimaryLogin,
isUsingMagicCode,
hasInitiatedSAMLLogin,
shouldShowAnotherLoginPageOpenedMessage,
credentials,
}: GetRenderOptionsParams): RenderOption {
const hasAccount = !isEmptyObject(account);
const isSAMLEnabled = !!account?.isSAMLEnabled;
const isSAMLRequired = !!account?.isSAMLRequired;
const hasEmailDeliveryFailure = !!account?.hasEmailDeliveryFailure;
// True, if the user has SAML required, and we haven't yet initiated SAML for their account
const shouldInitiateSAMLLogin = hasAccount && hasLogin && isSAMLRequired && !hasInitiatedSAMLLogin && !!account.isLoading;
const shouldShowChooseSSOOrMagicCode = hasAccount && hasLogin && isSAMLEnabled && !isSAMLRequired && !isUsingMagicCode;
// SAML required users may reload the login page after having already entered their login details, in which
// case we want to clear their sign in data so they don't end up in an infinite loop redirecting back to their
// SSO provider's login page
if (hasLogin && isSAMLRequired && !shouldInitiateSAMLLogin && !hasInitiatedSAMLLogin && !account.isLoading) {
Session.clearSignInData();
}
// Show the Welcome form if a user is signing up for a new account in a domain that is not controlled
const shouldShouldSignUpWelcomeForm = !!credentials?.login && !account?.validated && !account?.accountExists && !account?.domainControlled;
const shouldShowLoginForm = !shouldShowAnotherLoginPageOpenedMessage && !hasLogin && !hasValidateCode;
const shouldShowEmailDeliveryFailurePage = hasLogin && hasEmailDeliveryFailure && !shouldShowChooseSSOOrMagicCode && !shouldInitiateSAMLLogin;
const isUnvalidatedSecondaryLogin = hasLogin && !isPrimaryLogin && !account?.validated && !hasEmailDeliveryFailure;
const shouldShowValidateCodeForm =
!shouldShouldSignUpWelcomeForm &&
hasAccount &&
(hasLogin || hasValidateCode) &&
!isUnvalidatedSecondaryLogin &&
!hasEmailDeliveryFailure &&
!shouldShowChooseSSOOrMagicCode &&
!isSAMLRequired;
const shouldShowWelcomeHeader = shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || isUnvalidatedSecondaryLogin || shouldShouldSignUpWelcomeForm;
const shouldShowWelcomeText =
shouldShowLoginForm || shouldShowValidateCodeForm || shouldShowChooseSSOOrMagicCode || shouldShowAnotherLoginPageOpenedMessage || shouldShouldSignUpWelcomeForm;
return {
shouldShowLoginForm,
shouldShowEmailDeliveryFailurePage,
shouldShowUnlinkLoginForm: !shouldShouldSignUpWelcomeForm && isUnvalidatedSecondaryLogin,
shouldShowValidateCodeForm,
shouldShowChooseSSOOrMagicCode,
shouldInitiateSAMLLogin,
shouldShowWelcomeHeader,
shouldShowWelcomeText,
shouldShouldSignUpWelcomeForm,
};
}
const SignInPage = forwardRef(function SignInPage(
{credentials, account, activeClients = [], preferredLocale, shouldEnableMaxHeight = true, shouldShowBackButton}: SignInPageInnerProps,
ref: ForwardedRef<SignInPageRef>,
) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate, formatPhoneNumber} = useLocalize();
const {shouldUseNarrowLayout, isInNarrowPaneModal} = useResponsiveLayout();
const safeAreaInsets = useSafeAreaInsets();
const signInPageLayoutRef = useRef<SignInPageLayoutRef>(null);
const loginFormRef = useRef<InputHandle>(null);
const clearValidateCodeFormDataRef = useRef<() => void>(null);
/** This state is needed to keep track of if user is using recovery code instead of 2fa code,
* and we need it here since welcome text(`welcomeText`) also depends on it */
const [isUsingRecoveryCode, setIsUsingRecoveryCode] = useState(false);
/** This state is needed to keep track of whether the user has opted to use magic codes
* instead of signing in via SAML when SAML is enabled and not required */
const [isUsingMagicCode, setIsUsingMagicCode] = useState(false);
/** This state is needed to keep track of whether the user has been directed to their SSO provider's login page and
* if we need to clear their sign in details so they can enter a login */
const [hasInitiatedSAMLLogin, setHasInitiatedSAMLLogin] = useState(false);
const [login, setLogin] = useState(() => Str.removeSMSDomain(credentials?.login ?? ''));
const isClientTheLeader = !!activeClients && ActiveClientManager.isClientTheLeader();
// We need to show "Another login page is opened" message if the page isn't active and visible
// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowAnotherLoginPageOpenedMessage = Visibility.isVisible() && !isClientTheLeader;
useEffect(() => Performance.measureTTI(), []);
useEffect(() => {
if (preferredLocale) {
return;
}
App.setLocale(Localize.getDevicePreferredLocale());
}, [preferredLocale]);
useEffect(() => {
if (credentials?.login) {
return;
}
// If we don't have a login set, reset the user's SAML login preferences
if (isUsingMagicCode) {
setIsUsingMagicCode(false);
}
if (hasInitiatedSAMLLogin) {
setHasInitiatedSAMLLogin(false);
}
}, [credentials?.login, isUsingMagicCode, setIsUsingMagicCode, hasInitiatedSAMLLogin, setHasInitiatedSAMLLogin]);
const {
shouldShowLoginForm,
shouldShowEmailDeliveryFailurePage,
shouldShowUnlinkLoginForm,
shouldShowValidateCodeForm,
shouldShowChooseSSOOrMagicCode,
shouldInitiateSAMLLogin,
shouldShowWelcomeHeader,
shouldShowWelcomeText,
shouldShouldSignUpWelcomeForm,
} = getRenderOptions({
hasLogin: !!credentials?.login,
hasValidateCode: !!credentials?.validateCode,
account,
isPrimaryLogin: !account?.primaryLogin || account.primaryLogin === credentials?.login,
isUsingMagicCode,
hasInitiatedSAMLLogin,
shouldShowAnotherLoginPageOpenedMessage,
credentials,
});
if (shouldInitiateSAMLLogin) {
setHasInitiatedSAMLLogin(true);
Navigation.isNavigationReady().then(() => Navigation.navigate(ROUTES.SAML_SIGN_IN));
}
let welcomeHeader = '';
let welcomeText = '';
const headerText = translate('login.hero.header');
const userLogin = Str.removeSMSDomain(credentials?.login ?? '');
// replacing spaces with "hard spaces" to prevent breaking the number
const userLoginToDisplay = Str.isSMSLogin(userLogin) ? formatPhoneNumber(userLogin) : userLogin;
if (shouldShowAnotherLoginPageOpenedMessage) {
welcomeHeader = translate('welcomeText.anotherLoginPageIsOpen');
welcomeText = translate('welcomeText.anotherLoginPageIsOpenExplanation');
} else if (shouldShowLoginForm) {
welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.getStarted');
welcomeText = shouldUseNarrowLayout ? translate('welcomeText.getStarted') : '';
} else if (shouldShowValidateCodeForm) {
if (account?.requiresTwoFactorAuth) {
// We will only know this after a user signs in successfully, without their 2FA code
welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome');
welcomeText = isUsingRecoveryCode ? translate('validateCodeForm.enterRecoveryCode') : translate('validateCodeForm.enterAuthenticatorCode');
} else {
welcomeHeader = shouldUseNarrowLayout ? '' : translate('welcomeText.welcome');
welcomeText = shouldUseNarrowLayout
? `${translate('welcomeText.welcome')} ${translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay})}`
: translate('welcomeText.welcomeEnterMagicCode', {login: userLoginToDisplay});
}
} else if (shouldShowUnlinkLoginForm || shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome');
// Don't show any welcome text if we're showing the user the email delivery failed view
if (shouldShowEmailDeliveryFailurePage || shouldShowChooseSSOOrMagicCode) {
welcomeText = '';
}
} else if (shouldShouldSignUpWelcomeForm) {
welcomeHeader = shouldUseNarrowLayout ? headerText : translate('welcomeText.welcome');
welcomeText = shouldUseNarrowLayout
? `${translate('welcomeText.welcomeWithoutExclamation')} ${translate('welcomeText.welcomeNewFace', {login: userLoginToDisplay})}`
: translate('welcomeText.welcomeNewFace', {login: userLoginToDisplay});
} else if (!shouldInitiateSAMLLogin && !hasInitiatedSAMLLogin) {
Log.warn('SignInPage in unexpected state!');
}
const navigateFocus = () => {
signInPageLayoutRef.current?.scrollPageToTop();
loginFormRef.current?.clearDataAndFocus();
};
const navigateBack = () => {
if (
shouldShouldSignUpWelcomeForm ||
(!shouldShowAnotherLoginPageOpenedMessage && (shouldShowEmailDeliveryFailurePage || shouldShowUnlinkLoginForm || shouldShowChooseSSOOrMagicCode))
) {
Session.clearSignInData();
return;
}
if (shouldShowValidateCodeForm) {
clearValidateCodeFormDataRef?.current?.();
return;
}
Navigation.goBack();
};
useImperativeHandle(ref, () => ({
navigateBack,
}));
return (
// Bottom SafeAreaView is removed so that login screen svg displays correctly on mobile.
// The SVG should flow under the Home Indicator on iOS.
<ScreenWrapper
shouldShowOfflineIndicator={false}
shouldEnableMaxHeight={shouldEnableMaxHeight}
shouldUseCachedViewportHeight
style={[styles.signInPage, StyleUtils.getSafeAreaPadding({...safeAreaInsets, bottom: 0, top: isInNarrowPaneModal ? 0 : safeAreaInsets.top}, 1)]}
testID={SignInPageThemeWrapper.displayName}
>
{shouldShowBackButton && <HeaderWithBackButton onBackButtonPress={navigateBack} />}
<SignInPageLayout
welcomeHeader={welcomeHeader}
welcomeText={welcomeText}
shouldShowWelcomeHeader={shouldShowWelcomeHeader || !shouldUseNarrowLayout}
shouldShowWelcomeText={shouldShowWelcomeText}
ref={signInPageLayoutRef}
navigateFocus={navigateFocus}
>
{/* LoginForm must use the isVisible prop. This keeps it mounted, but visually hidden
so that password managers can access the values. Conditionally rendering this component will break this feature. */}
<LoginForm
ref={loginFormRef}
isVisible={shouldShowLoginForm}
login={login}
onLoginChanged={setLogin}
blurOnSubmit={account?.validated === false}
scrollPageToTop={signInPageLayoutRef.current?.scrollPageToTop}
/>
{shouldShouldSignUpWelcomeForm && <SignUpWelcomeForm />}
{shouldShowValidateCodeForm && (
<ValidateCodeForm
isVisible={!shouldShowAnotherLoginPageOpenedMessage}
isUsingRecoveryCode={isUsingRecoveryCode}
setIsUsingRecoveryCode={setIsUsingRecoveryCode}
setClearSignInData={(clearSignInData: () => void) => {
(clearValidateCodeFormDataRef.current as (() => void) | null) = clearSignInData;
}}
/>
)}
{!shouldShowAnotherLoginPageOpenedMessage && (
<>
{shouldShowUnlinkLoginForm && <UnlinkLoginForm />}
{shouldShowChooseSSOOrMagicCode && <ChooseSSOOrMagicCode setIsUsingMagicCode={setIsUsingMagicCode} />}
{shouldShowEmailDeliveryFailurePage && <EmailDeliveryFailurePage />}
</>
)}
</SignInPageLayout>
</ScreenWrapper>
);
});
type SignInPageProps = SignInPageInnerProps;
type SignInPageOnyxProps = SignInPageInnerOnyxProps;
function SignInPageThemeWrapper(props: SignInPageProps, ref: ForwardedRef<SignInPageRef>) {
return (
<ThemeProvider theme={CONST.THEME.DARK}>
<ThemeStylesProvider>
<ColorSchemeWrapper>
<CustomStatusBarAndBackground isNested />
<SignInPage
ref={ref}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</ColorSchemeWrapper>
</ThemeStylesProvider>
</ThemeProvider>
);
}
SignInPageThemeWrapper.displayName = 'SignInPage';
export default withOnyx<SignInPageProps, SignInPageOnyxProps>({
account: {key: ONYXKEYS.ACCOUNT},
credentials: {key: ONYXKEYS.CREDENTIALS},
/**
This variable is only added to make sure the component is re-rendered
whenever the activeClients change, so that we call the
ActiveClientManager.isClientTheLeader function
everytime the leader client changes.
We use that function to prevent repeating code that checks which client is the leader.
*/
activeClients: {key: ONYXKEYS.ACTIVE_CLIENTS},
preferredLocale: {
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
},
})(forwardRef(SignInPageThemeWrapper)); |
e6fb313
to
febd1ef
Compare
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@ishpaul777, I have implemented useImperativeHandle in both components, and it works well on all platforms, likely including the desktop. The header button spacing should also be fixed because I added the HeaderWithBackbutton in SignInModal. All recordings have been added except for the Desktop App. I can't open the room on the desktop app now. Please let me know if you have any ideas about that. I've tried after clearing all the data. Code changes are ready for review. Monosnap.screencast.2024-08-11.19-02-20.mp4 |
onPress={() => { | ||
if (Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { | ||
Session.clearSignInData(); | ||
return; | ||
} | ||
redirectToSignIn(); | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can just call Session.clearSignInData()
like we do at other place, i dont think we ever need redirectToSignIn and clear all onyx data 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redirectToSignIn()
is being used till now so I only added the change for the modal, can you pls look at #46760 (comment)? after that I will check this out and will remove redirectToSignIn() if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gentle bump here @Krishna2323
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will finish this today. Thanks for the ping
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ishpaul777, code updated.
i am also not able to sign in as anonymous user. There is no support for that on desktop App. Lines 451 to 455 in 6839fcb
|
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
src/pages/signin/SignInPage.tsx
Outdated
@@ -272,6 +302,7 @@ function SignInPage({credentials, account, activeClients = [], preferredLocale, | |||
style={[styles.signInPage, StyleUtils.getSafeAreaPadding({...safeAreaInsets, bottom: 0, top: isInNarrowPaneModal ? 0 : safeAreaInsets.top}, 1)]} | |||
testID={SignInPageThemeWrapper.displayName} | |||
> | |||
{/* {shouldShowBackButton && <HeaderWithBackButton onBackButtonPress={navigateBack} />} */} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for that. Removed.
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a NAB comment Rest LGTM!
src/pages/signin/SignInPage.tsx
Outdated
@@ -141,14 +147,19 @@ function getRenderOptions({ | |||
}; | |||
} | |||
|
|||
function SignInPage({credentials, account, activeClients = [], preferredLocale, shouldEnableMaxHeight = true}: SignInPageInnerProps) { | |||
const SignInPage = forwardRef(function SignInPage( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB but it would be nice if we can create new variable SignInPageWithRef= forwardRef(SignInPage)
to clean this up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do that cc @Krishna2323
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will be on it in few moments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ishpaul777 @luacmartins, updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.0.22-0 🚀
|
🚀 Deployed to staging by https://github.com/luacmartins in version: 9.0.22-1 🚀
|
🚀 Deployed to production by https://github.com/chiragsalian in version: 9.0.22-9 🚀
|
Details
Fixed Issues
$ #46106
PROPOSAL: #46106 (comment)
Tests
123@gmail.com
and continueOffline tests
123@gmail.com
and continueQA Steps
123@gmail.com
and continuePR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)myBool && <MyComponent />
.src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
android_native.mp4
Android: mWeb Chrome
android_chrome.mp4
iOS: Native
ios_native.mp4
iOS: mWeb Safari
ios_safari.mp4
MacOS: Chrome / Safari
web_chrome.mp4
MacOS: Desktop