Skip to content

Commit b1efab3

Browse files
Merge pull request Expensify#73479 from dmkt9/fix/72890
Fix/72890 - FAB shows the contrasting color when hovered after changing app theme
2 parents a383ddd + 2901632 commit b1efab3

2 files changed

Lines changed: 42 additions & 19 deletions

File tree

src/components/FloatingActionButton.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe
5656
const fabSize = isLHBVisible ? variables.iconSizeSmall : variables.iconSizeNormal;
5757

5858
const sharedValue = useSharedValue(isActive ? 1 : 0);
59+
const isHovered = useSharedValue(false);
5960
const buttonRef = ref;
6061

6162
useEffect(() => {
@@ -68,7 +69,7 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe
6869
}, [isActive, sharedValue]);
6970

7071
const animatedStyle = useAnimatedStyle(() => {
71-
const backgroundColor = interpolateColor(sharedValue.get(), [0, 1], [buttonDefaultBG, buttonHoveredBG]);
72+
const backgroundColor = isHovered.get() && !sharedValue.get() ? buttonHoveredBG : interpolateColor(sharedValue.get(), [0, 1], [buttonDefaultBG, buttonHoveredBG]);
7273

7374
return {
7475
transform: [{rotate: `${sharedValue.get() * 135}deg`}],
@@ -115,22 +116,26 @@ function FloatingActionButton({onPress, onLongPress, isActive, accessibilityLabe
115116
shouldUseHapticsOnLongPress
116117
testID="floating-action-button"
117118
>
118-
{({hovered}) => (
119-
<Animated.View
120-
style={[styles.floatingActionButton, {borderRadius}, styles.floatingActionButtonSmall, animatedStyle, hovered && {backgroundColor: buttonHoveredBG}]}
121-
testID="fab-animated-container"
122-
>
123-
<Svg
124-
width={fabSize}
125-
height={fabSize}
119+
{({hovered}) => {
120+
isHovered.set(hovered);
121+
122+
return (
123+
<Animated.View
124+
style={[styles.floatingActionButton, {borderRadius}, styles.floatingActionButtonSmall, animatedStyle]}
125+
testID="fab-animated-container"
126126
>
127-
<AnimatedPath
128-
d={isLHBVisible ? SMALL_FAB_PATH : FAB_PATH}
129-
fill={icon}
130-
/>
131-
</Svg>
132-
</Animated.View>
133-
)}
127+
<Svg
128+
width={fabSize}
129+
height={fabSize}
130+
>
131+
<AnimatedPath
132+
d={isLHBVisible ? SMALL_FAB_PATH : FAB_PATH}
133+
fill={icon}
134+
/>
135+
</Svg>
136+
</Animated.View>
137+
);
138+
}}
134139
</PressableWithoutFeedback>
135140
);
136141
}

tests/ui/FloatingActionButtonTest.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,26 @@ const mockedUseResponsiveLayout = useResponsiveLayout as jest.MockedFunction<typ
2727
// Mock useIsHomeRouteActive to avoid navigation state issues
2828
jest.mock('@navigation/helpers/useIsHomeRouteActive', () => (): boolean => false);
2929

30+
let mockUseAnimatedStyleUpdater: () => Record<string, unknown>;
3031
// Silence react-native-reanimated warnings in Jest
3132
jest.mock('react-native-reanimated', () => {
3233
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
33-
return require('react-native-reanimated/mock');
34+
return {
35+
...require('react-native-reanimated/mock'),
36+
interpolateColor: (value: number, input: number[], output: string[]) => {
37+
const [, inputMax] = input;
38+
const [colorMin, colorMax] = output;
39+
40+
if (value >= inputMax) {
41+
return colorMax;
42+
}
43+
return colorMin;
44+
},
45+
useAnimatedStyle: (updater: () => Record<string, unknown>) => {
46+
mockUseAnimatedStyleUpdater = updater;
47+
return updater();
48+
},
49+
};
3450
});
3551

3652
describe('FloatingActionButton hover', () => {
@@ -67,13 +83,15 @@ describe('FloatingActionButton hover', () => {
6783
// Before hover, should not have successHover background
6884
expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.productDark500});
6985

86+
expect(mockUseAnimatedStyleUpdater()).not.toEqual(expect.objectContaining({backgroundColor: colors.productDark500}));
87+
7088
// Test hover in
7189
fireEvent(fab, 'hoverIn');
72-
expect(animatedContainer).toHaveStyle({backgroundColor: colors.productDark500});
90+
expect(mockUseAnimatedStyleUpdater()).toEqual(expect.objectContaining({backgroundColor: colors.productDark500}));
7391

7492
// Test hover out
7593
fireEvent(fab, 'hoverOut');
76-
expect(animatedContainer).not.toHaveStyle({backgroundColor: colors.productDark500});
94+
expect(mockUseAnimatedStyleUpdater()).not.toEqual(expect.objectContaining({backgroundColor: colors.productDark500}));
7795
});
7896

7997
it('should render animated button if LHB is visible', () => {

0 commit comments

Comments
 (0)