From 7598b61d92736277ff1840fa52b7575905d5d780 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 17:28:40 +0530 Subject: [PATCH 01/11] feat: add hover effects to navigation tab bar and floating action button - Add hover state styling to navigation tab bar items with background color change - Add hover effect to floating action button with success hover background - Add hover effect to navigation tab bar avatar with green ring indicator - Add testID attributes for better testability - Add comprehensive unit tests for hover functionality - Add navigationTabBarItemHovered style to theme --- src/components/FloatingActionButton.tsx | 30 ++-- .../Navigation/NavigationTabBar/index.tsx | 163 ++++++++++-------- .../home/sidebar/NavigationTabBarAvatar.tsx | 63 ++++--- .../sidebar/ProfileAvatarWithIndicator.tsx | 5 +- src/styles/index.ts | 8 + tests/ui/FloatingActionButtonTest.tsx | 61 +++++++ tests/ui/NavigationTabBarAvatarTest.tsx | 54 ++++++ 7 files changed, 276 insertions(+), 108 deletions(-) create mode 100644 tests/ui/FloatingActionButtonTest.tsx create mode 100644 tests/ui/NavigationTabBarAvatarTest.tsx diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index 1fdd88cc445d..3ca8370c8436 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -48,7 +48,7 @@ type FloatingActionButtonProps = { }; function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabel, role, isTooltipAllowed, ref}: FloatingActionButtonProps) { - const {success, buttonDefaultBG, textLight} = useTheme(); + const {success, successHover, buttonDefaultBG, textLight} = useTheme(); const styles = useThemeStyles(); const borderRadius = styles.floatingActionButton.borderRadius; const fabPressable = useRef(null); @@ -128,7 +128,7 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe buttonRef.current = el ?? null; } }} - style={[ + style={(state) => [ styles.h100, styles.navigationTabBarItem, @@ -142,17 +142,23 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe shouldUseHapticsOnLongPress testID="floating-action-button" > - - ( + - - - + + + + + )} ); diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 503d5c3090d6..3bca73f83374 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -227,89 +227,114 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={navigateToChats} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.inbox')} - style={styles.leftNavigationTabBarItem} + testID="inbox-tab-button" + style={({hovered}) => [ + styles.leftNavigationTabBarItem, + hovered && styles.navigationTabBarItemHovered, + ]} > - - - {!!chatTabBrickRoad && ( - - )} - - - {translate('common.inbox')} - + {({hovered}) => ( + <> + + + {!!chatTabBrickRoad && ( + + )} + + + {translate('common.inbox')} + + + )} [ + styles.leftNavigationTabBarItem, + hovered && styles.navigationTabBarItemHovered, + ]} > - - - - - {translate('common.reports')} - + {({hovered}) => ( + <> + + + + + {translate('common.reports')} + + + )} [ + styles.leftNavigationTabBarItem, + hovered && styles.navigationTabBarItemHovered, + ]} > - - - {!!workspacesTabIndicatorStatus && } - - - {translate('common.workspacesTabTitle')} - + {({hovered}) => ( + <> + + + {!!workspacesTabIndicatorStatus && } + + + {translate('common.workspacesTabTitle')} + + + )} { + if (delegateEmail) { + return ( + + ); + } - if (delegateEmail) { - children = ( - - ); - } else if (emojiStatus) { - children = ( - - ); - } else { - children = ( + if (emojiStatus) { + return ( + + ); + } + + return ( ); - } + }; return ( [style, hovered && styles.navigationTabBarItemHovered]} > - {children} - - {translate('initialSettingsPage.account')} - + {({hovered}) => ( + <> + {renderAvatar(isSelected || hovered)} + + {translate('initialSettingsPage.account')} + + + )} ); } diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx index 54910c93e3ee..fda8459a798b 100644 --- a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx +++ b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx @@ -28,7 +28,10 @@ function ProfileAvatarWithIndicator({isSelected = false, containerStyles}: Profi style={containerStyles} > - + paddingHorizontal: 4, }, + /** + * Background style applied to navigation tab bar items when they are hovered. + * Do not apply for the active/selected state, those already have their own styling. + */ + navigationTabBarItemHovered: { + backgroundColor: theme.sidebarHover, + }, + leftNavigationTabBarContainer: { height: '100%', width: variables.navigationTabBarSize, diff --git a/tests/ui/FloatingActionButtonTest.tsx b/tests/ui/FloatingActionButtonTest.tsx new file mode 100644 index 000000000000..dad260516168 --- /dev/null +++ b/tests/ui/FloatingActionButtonTest.tsx @@ -0,0 +1,61 @@ +import {fireEvent, render, screen} from '@testing-library/react-native'; +import React from 'react'; +import {NavigationContainer} from '@react-navigation/native'; +import FloatingActionButton from '@components/FloatingActionButton'; +import CONST from '@src/CONST'; +import lightTheme from '@styles/theme/themes/light'; + +// FloatingActionButton relies on ProductTrainingContext, so provide a minimal mock. +jest.mock('@components/ProductTrainingContext', () => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention + useProductTrainingContext: () => ({ + renderProductTrainingTooltip: () => null, + shouldShowProductTrainingTooltip: false, + hideProductTrainingTooltip: () => {}, + }), +})); + +// useResponsiveLayout determines LHB visibility. Mock a wide layout to keep behaviour deterministic. +jest.mock('@hooks/useResponsiveLayout', () => () => ({shouldUseNarrowLayout: false})); + +// Mock useIsHomeRouteActive to avoid navigation state issues +jest.mock('@navigation/helpers/useIsHomeRouteActive', () => () => false); + +// Silence react-native-reanimated warnings in Jest +jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); + +describe('FloatingActionButton hover', () => { + const onPress = jest.fn(); + + const renderFAB = () => + render( + + + , + ); + + it('changes background colour on hover', () => { + renderFAB(); + const fab = screen.getByTestId('floating-action-button'); + + // Get the animated container by testID + const animatedContainer = screen.getByTestId('fab-animated-container'); + + // Before hover, should not have successHover background + expect(animatedContainer).not.toHaveStyle({backgroundColor: lightTheme.successHover}); + + // Test hover in + fireEvent(fab, 'hoverIn'); + expect(animatedContainer).toHaveStyle({backgroundColor: lightTheme.successHover}); + + // Test hover out + fireEvent(fab, 'hoverOut'); + expect(animatedContainer).not.toHaveStyle({backgroundColor: lightTheme.successHover}); + }); +}); diff --git a/tests/ui/NavigationTabBarAvatarTest.tsx b/tests/ui/NavigationTabBarAvatarTest.tsx new file mode 100644 index 000000000000..eaf0056b7e9f --- /dev/null +++ b/tests/ui/NavigationTabBarAvatarTest.tsx @@ -0,0 +1,54 @@ +import {fireEvent, render, screen} from '@testing-library/react-native'; +import React from 'react'; +import OnyxListItemProvider from '@components/OnyxListItemProvider'; +import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; +import CONST from '@src/CONST'; +import lightTheme from '@styles/theme/themes/light'; + +// Mock responsive layout to force wide layout +jest.mock('@hooks/useResponsiveLayout', () => () => ({shouldUseNarrowLayout: false})); + +// Silence reanimated warnings +jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); + +describe('NavigationTabBarAvatar hover', () => { + const onPress = jest.fn(); + + const renderAvatar = () => + render( + + + , + ); + + it('shows green ring while hovered', () => { + renderAvatar(); + const button = screen.getByRole(CONST.ROLE.BUTTON); + + // Before hover, ring should not have border styles + const ring = screen.getByTestId('avatar-ring'); + expect(ring).not.toHaveStyle({ + borderColor: lightTheme.success, + borderWidth: 2, + }); + + fireEvent(button, 'hoverIn'); + + // After hover, ring should have correct styles + expect(ring).toHaveStyle({ + borderColor: lightTheme.success, + borderWidth: 2, + }); + + fireEvent(button, 'hoverOut'); + expect(ring).not.toHaveStyle({ + borderColor: lightTheme.success, + borderWidth: 2, + }); + }); +}); From 365e0b9971eda50ba4b9a7d0ca6a254ed6a98963 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 17:36:24 +0530 Subject: [PATCH 02/11] remove unused testids --- src/components/Navigation/NavigationTabBar/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 3bca73f83374..dae53c4257a1 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -227,7 +227,6 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={navigateToChats} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.inbox')} - testID="inbox-tab-button" style={({hovered}) => [ styles.leftNavigationTabBarItem, hovered && styles.navigationTabBarItemHovered, @@ -270,7 +269,6 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={navigateToSearch} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.reports')} - testID="search-tab-button" style={({hovered}) => [ styles.leftNavigationTabBarItem, hovered && styles.navigationTabBarItemHovered, From 4b39f6a67dc1090170bbf3751c9057f3994497c9 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 17:54:24 +0530 Subject: [PATCH 03/11] remove unused state variable --- src/components/FloatingActionButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index 3ca8370c8436..c1491fca6004 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -128,7 +128,7 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe buttonRef.current = el ?? null; } }} - style={(state) => [ + style={[ styles.h100, styles.navigationTabBarItem, From 286f9544f6dc5cda1df0f5e7f4b47f099a9ec63e Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 18:05:44 +0530 Subject: [PATCH 04/11] fix linting issues --- .../Navigation/NavigationTabBar/index.tsx | 16 ++++++++++--- tests/ui/FloatingActionButtonTest.tsx | 24 ++++++++++++------- tests/ui/NavigationTabBarAvatarTest.tsx | 16 ++++++++----- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index dae53c4257a1..b3e18e364a46 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -53,6 +53,16 @@ type NavigationTabBarProps = { function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar = false}: NavigationTabBarProps) { const theme = useTheme(); const styles = useThemeStyles(); + + const getIconFill = useCallback((isSelected: boolean, isHovered: boolean) => { + if (isSelected) { + return theme.iconMenu; + } + if (isHovered) { + return theme.successHover; + } + return theme.icon; + }, [theme]); const {translate, preferredLocale} = useLocalize(); const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus(); const {orderedReports} = useSidebarOrderedReports(); @@ -237,7 +247,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar @@ -279,7 +289,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar @@ -313,7 +323,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar diff --git a/tests/ui/FloatingActionButtonTest.tsx b/tests/ui/FloatingActionButtonTest.tsx index dad260516168..0382b0d64500 100644 --- a/tests/ui/FloatingActionButtonTest.tsx +++ b/tests/ui/FloatingActionButtonTest.tsx @@ -3,12 +3,17 @@ import React from 'react'; import {NavigationContainer} from '@react-navigation/native'; import FloatingActionButton from '@components/FloatingActionButton'; import CONST from '@src/CONST'; -import lightTheme from '@styles/theme/themes/light'; +import colors from '@styles/theme/colors'; + // FloatingActionButton relies on ProductTrainingContext, so provide a minimal mock. jest.mock('@components/ProductTrainingContext', () => ({ // eslint-disable-next-line @typescript-eslint/naming-convention - useProductTrainingContext: () => ({ + useProductTrainingContext: (): { + renderProductTrainingTooltip: () => null; + shouldShowProductTrainingTooltip: boolean; + hideProductTrainingTooltip: () => void; + } => ({ renderProductTrainingTooltip: () => null, shouldShowProductTrainingTooltip: false, hideProductTrainingTooltip: () => {}, @@ -16,13 +21,16 @@ jest.mock('@components/ProductTrainingContext', () => ({ })); // useResponsiveLayout determines LHB visibility. Mock a wide layout to keep behaviour deterministic. -jest.mock('@hooks/useResponsiveLayout', () => () => ({shouldUseNarrowLayout: false})); +jest.mock('@hooks/useResponsiveLayout', () => (): {shouldUseNarrowLayout: boolean} => ({shouldUseNarrowLayout: false})); // Mock useIsHomeRouteActive to avoid navigation state issues -jest.mock('@navigation/helpers/useIsHomeRouteActive', () => () => false); +jest.mock('@navigation/helpers/useIsHomeRouteActive', () => (): boolean => false); // Silence react-native-reanimated warnings in Jest -jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); +jest.mock('react-native-reanimated', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return require('react-native-reanimated/mock'); +}); describe('FloatingActionButton hover', () => { const onPress = jest.fn(); @@ -48,14 +56,14 @@ describe('FloatingActionButton hover', () => { const animatedContainer = screen.getByTestId('fab-animated-container'); // Before hover, should not have successHover background - expect(animatedContainer).not.toHaveStyle({backgroundColor: lightTheme.successHover}); + expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.greenHover}); // Test hover in fireEvent(fab, 'hoverIn'); - expect(animatedContainer).toHaveStyle({backgroundColor: lightTheme.successHover}); + expect(animatedContainer).toHaveStyle({backgroundColor: colors.greenHover}); // Test hover out fireEvent(fab, 'hoverOut'); - expect(animatedContainer).not.toHaveStyle({backgroundColor: lightTheme.successHover}); + expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.greenHover}); }); }); diff --git a/tests/ui/NavigationTabBarAvatarTest.tsx b/tests/ui/NavigationTabBarAvatarTest.tsx index eaf0056b7e9f..b5b8cba5ed34 100644 --- a/tests/ui/NavigationTabBarAvatarTest.tsx +++ b/tests/ui/NavigationTabBarAvatarTest.tsx @@ -3,13 +3,17 @@ import React from 'react'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; import CONST from '@src/CONST'; -import lightTheme from '@styles/theme/themes/light'; +import colors from '@styles/theme/colors'; + // Mock responsive layout to force wide layout -jest.mock('@hooks/useResponsiveLayout', () => () => ({shouldUseNarrowLayout: false})); +jest.mock('@hooks/useResponsiveLayout', () => (): {shouldUseNarrowLayout: boolean} => ({shouldUseNarrowLayout: false})); // Silence reanimated warnings -jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')); +jest.mock('react-native-reanimated', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return require('react-native-reanimated/mock'); +}); describe('NavigationTabBarAvatar hover', () => { const onPress = jest.fn(); @@ -33,7 +37,7 @@ describe('NavigationTabBarAvatar hover', () => { // Before hover, ring should not have border styles const ring = screen.getByTestId('avatar-ring'); expect(ring).not.toHaveStyle({ - borderColor: lightTheme.success, + borderColor: colors.green400, borderWidth: 2, }); @@ -41,13 +45,13 @@ describe('NavigationTabBarAvatar hover', () => { // After hover, ring should have correct styles expect(ring).toHaveStyle({ - borderColor: lightTheme.success, + borderColor: colors.green400, borderWidth: 2, }); fireEvent(button, 'hoverOut'); expect(ring).not.toHaveStyle({ - borderColor: lightTheme.success, + borderColor: colors.green400, borderWidth: 2, }); }); From 7b76042fae18149fad966c205db2e9ffcdbd93ae Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 20:55:43 +0530 Subject: [PATCH 05/11] prettier --- src/components/FloatingActionButton.tsx | 10 ++++-- .../Navigation/NavigationTabBar/index.tsx | 36 ++++++++----------- .../home/sidebar/NavigationTabBarAvatar.tsx | 4 +-- .../sidebar/ProfileAvatarWithIndicator.tsx | 6 ++-- tests/ui/FloatingActionButtonTest.tsx | 9 +++-- tests/ui/NavigationTabBarAvatarTest.tsx | 3 +- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index c1491fca6004..3d5d0dfde08f 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -154,8 +154,14 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe ]} testID="fab-animated-container" > - - + + )} diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index b3e18e364a46..8a3e8476ea63 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -54,15 +54,18 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar const theme = useTheme(); const styles = useThemeStyles(); - const getIconFill = useCallback((isSelected: boolean, isHovered: boolean) => { - if (isSelected) { - return theme.iconMenu; - } - if (isHovered) { - return theme.successHover; - } - return theme.icon; - }, [theme]); + const getIconFill = useCallback( + (isSelected: boolean, isHovered: boolean) => { + if (isSelected) { + return theme.iconMenu; + } + if (isHovered) { + return theme.successHover; + } + return theme.icon; + }, + [theme], + ); const {translate, preferredLocale} = useLocalize(); const {indicatorColor: workspacesTabIndicatorColor, status: workspacesTabIndicatorStatus} = useWorkspacesTabIndicatorStatus(); const {orderedReports} = useSidebarOrderedReports(); @@ -237,10 +240,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={navigateToChats} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.inbox')} - style={({hovered}) => [ - styles.leftNavigationTabBarItem, - hovered && styles.navigationTabBarItemHovered, - ]} + style={({hovered}) => [styles.leftNavigationTabBarItem, hovered && styles.navigationTabBarItemHovered]} > {({hovered}) => ( <> @@ -279,10 +279,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={navigateToSearch} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.reports')} - style={({hovered}) => [ - styles.leftNavigationTabBarItem, - hovered && styles.navigationTabBarItemHovered, - ]} + style={({hovered}) => [styles.leftNavigationTabBarItem, hovered && styles.navigationTabBarItemHovered]} > {({hovered}) => ( <> @@ -313,10 +310,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar onPress={showWorkspaces} role={CONST.ROLE.BUTTON} accessibilityLabel={translate('common.workspacesTabTitle')} - style={({hovered}) => [ - styles.leftNavigationTabBarItem, - hovered && styles.navigationTabBarItemHovered, - ]} + style={({hovered}) => [styles.leftNavigationTabBarItem, hovered && styles.navigationTabBarItemHovered]} > {({hovered}) => ( <> diff --git a/src/pages/home/sidebar/NavigationTabBarAvatar.tsx b/src/pages/home/sidebar/NavigationTabBarAvatar.tsx index 18b8e133d5bc..c15a74531542 100644 --- a/src/pages/home/sidebar/NavigationTabBarAvatar.tsx +++ b/src/pages/home/sidebar/NavigationTabBarAvatar.tsx @@ -75,9 +75,7 @@ function NavigationTabBarAvatar({onPress, isSelected = false, style}: Navigation {({hovered}) => ( <> {renderAvatar(isSelected || hovered)} - + {translate('initialSettingsPage.account')} diff --git a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx index fda8459a798b..fc29205afed2 100644 --- a/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx +++ b/src/pages/home/sidebar/ProfileAvatarWithIndicator.tsx @@ -20,7 +20,7 @@ type ProfileAvatarWithIndicatorProps = { function ProfileAvatarWithIndicator({isSelected = false, containerStyles}: ProfileAvatarWithIndicatorProps) { const styles = useThemeStyles(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [isLoading = true] = useOnyx(ONYXKEYS.IS_LOADING_APP); + const [isLoading = true] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); return ( - ({ @@ -57,11 +56,11 @@ describe('FloatingActionButton hover', () => { // Before hover, should not have successHover background expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.greenHover}); - + // Test hover in fireEvent(fab, 'hoverIn'); expect(animatedContainer).toHaveStyle({backgroundColor: colors.greenHover}); - + // Test hover out fireEvent(fab, 'hoverOut'); expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.greenHover}); diff --git a/tests/ui/NavigationTabBarAvatarTest.tsx b/tests/ui/NavigationTabBarAvatarTest.tsx index b5b8cba5ed34..108b0466bcd0 100644 --- a/tests/ui/NavigationTabBarAvatarTest.tsx +++ b/tests/ui/NavigationTabBarAvatarTest.tsx @@ -2,9 +2,8 @@ import {fireEvent, render, screen} from '@testing-library/react-native'; import React from 'react'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; import NavigationTabBarAvatar from '@pages/home/sidebar/NavigationTabBarAvatar'; -import CONST from '@src/CONST'; import colors from '@styles/theme/colors'; - +import CONST from '@src/CONST'; // Mock responsive layout to force wide layout jest.mock('@hooks/useResponsiveLayout', () => (): {shouldUseNarrowLayout: boolean} => ({shouldUseNarrowLayout: false})); From 4a9bdee929d84784d8a73cc2110905c61eeef791 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Mon, 25 Aug 2025 21:06:59 +0530 Subject: [PATCH 06/11] fix cspell issue --- tests/ui/FloatingActionButtonTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/FloatingActionButtonTest.tsx b/tests/ui/FloatingActionButtonTest.tsx index 716cb7b87437..cf85cb66cdf6 100644 --- a/tests/ui/FloatingActionButtonTest.tsx +++ b/tests/ui/FloatingActionButtonTest.tsx @@ -47,7 +47,7 @@ describe('FloatingActionButton hover', () => { , ); - it('changes background colour on hover', () => { + it('changes background color on hover', () => { renderFAB(); const fab = screen.getByTestId('floating-action-button'); From 41f063e75bf14cfc3861d4c1a525cbe0e86dedd6 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Tue, 26 Aug 2025 09:28:52 +0530 Subject: [PATCH 07/11] Change hover icon color to sucess instead of success-hover --- src/components/Navigation/NavigationTabBar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 8a3e8476ea63..6f180337b9d8 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -60,7 +60,7 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar return theme.iconMenu; } if (isHovered) { - return theme.successHover; + return theme.success; } return theme.icon; }, From c88ac38b56668700d5ae13927f213c898001171f Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Wed, 27 Aug 2025 23:04:56 +0530 Subject: [PATCH 08/11] Add border color on-hover for the outline around the green/red dot --- .../Navigation/NavigationTabBar/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Navigation/NavigationTabBar/index.tsx b/src/components/Navigation/NavigationTabBar/index.tsx index 6f180337b9d8..3eb2c79c83c4 100644 --- a/src/components/Navigation/NavigationTabBar/index.tsx +++ b/src/components/Navigation/NavigationTabBar/index.tsx @@ -253,9 +253,12 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar /> {!!chatTabBrickRoad && ( )} @@ -321,7 +324,9 @@ function NavigationTabBar({selectedTab, isTooltipAllowed = false, isTopLevelBar width={variables.iconBottomBar} height={variables.iconBottomBar} /> - {!!workspacesTabIndicatorStatus && } + {!!workspacesTabIndicatorStatus && ( + + )} Date: Thu, 4 Sep 2025 20:21:31 +0530 Subject: [PATCH 09/11] use hovered prop directly Co-authored-by: Getabalew <75031127+getusha@users.noreply.github.com> --- src/components/FloatingActionButton.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index 3d5d0dfde08f..f478a37706da 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -142,15 +142,14 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe shouldUseHapticsOnLongPress testID="floating-action-button" > - {(state) => ( + {({hovered}) => ( From 71f44e185454d4f590dbef5a467b6357fd1d785f Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Thu, 4 Sep 2025 20:26:51 +0530 Subject: [PATCH 10/11] make the JSDoc comment more descriptive --- src/pages/home/sidebar/NavigationTabBarAvatar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/home/sidebar/NavigationTabBarAvatar.tsx b/src/pages/home/sidebar/NavigationTabBarAvatar.tsx index c15a74531542..423f934b4369 100644 --- a/src/pages/home/sidebar/NavigationTabBarAvatar.tsx +++ b/src/pages/home/sidebar/NavigationTabBarAvatar.tsx @@ -33,7 +33,8 @@ function NavigationTabBarAvatar({onPress, isSelected = false, style}: Navigation const emojiStatus = currentUserPersonalDetails?.status?.emojiCode ?? ''; /** - * Returns the avatar element with the correct active (ring) state. + * Renders the appropriate avatar component based on user state (delegate, emoji status, or default profile) + * with the correct active (ring) state for selection and hover effects. */ const renderAvatar = (active: boolean) => { if (delegateEmail) { From ed5d0d3a7fcaeb2152bd1b9a730eefb4befc4bf2 Mon Sep 17 00:00:00 2001 From: cosmicvulpes Date: Thu, 4 Sep 2025 20:30:08 +0530 Subject: [PATCH 11/11] fix prettier diff --- src/components/FloatingActionButton.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/FloatingActionButton.tsx b/src/components/FloatingActionButton.tsx index f478a37706da..494e558922cc 100644 --- a/src/components/FloatingActionButton.tsx +++ b/src/components/FloatingActionButton.tsx @@ -144,13 +144,7 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe > {({hovered}) => (