Skip to content

Commit 0abe77e

Browse files
committed
refactor: self review
1 parent 0d094a6 commit 0abe77e

5 files changed

Lines changed: 52 additions & 12 deletions

File tree

‎src/components/SegmentedButtons/AnimatedOptionIcon.tsx‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StyleSheet } from 'react-native';
1+
import { StyleSheet, View } from 'react-native';
22
import type { TextStyle } from 'react-native';
33

44
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
@@ -17,22 +17,23 @@ type Props = {
1717
testID?: string;
1818
};
1919

20-
const AnimatedOptionIcon = ({
21-
animated,
20+
type AnimatedIconProps = Omit<Props, 'animated'>;
21+
22+
const AnimatedIcon = ({
2223
color,
2324
opacity,
2425
scale,
2526
source,
2627
testID,
27-
}: Props) => {
28+
}: AnimatedIconProps) => {
2829
const animatedStyle = useAnimatedStyle(() => ({
2930
transform: [{ scale: 1 - scale.value }],
3031
}));
3132

3233
return (
3334
<Animated.View
3435
testID={testID}
35-
style={[styles.icon, { opacity }, animated ? animatedStyle : undefined]}
36+
style={[styles.icon, { opacity }, animatedStyle]}
3637
>
3738
<Icon
3839
source={source}
@@ -43,6 +44,24 @@ const AnimatedOptionIcon = ({
4344
);
4445
};
4546

47+
const AnimatedOptionIcon = ({ animated, ...props }: Props) => {
48+
if (animated) {
49+
return <AnimatedIcon {...props} />;
50+
}
51+
52+
const { color, opacity, source, testID } = props;
53+
54+
return (
55+
<View testID={testID} style={[styles.icon, { opacity }]}>
56+
<Icon
57+
source={source}
58+
size={SegmentedButtonTokens.iconSize}
59+
color={color}
60+
/>
61+
</View>
62+
);
63+
};
64+
4665
const styles = StyleSheet.create({
4766
icon: {
4867
width: SegmentedButtonTokens.iconSize,

‎src/components/SegmentedButtons/SegmentedButtonContent.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const SegmentedButtonContent = ({
4949
const optionIcon = icon && (!label || !showCheckIcon) ? icon : undefined;
5050

5151
const reduceMotion = useReduceMotion();
52-
const checkmarkScale = useSharedValue(checked ? 1 : 0);
52+
const checkmarkScale = useSharedValue(showCheckIcon ? 1 : 0);
5353

5454
React.useEffect(() => {
5555
checkmarkScale.value = withSpring(showCheckIcon ? 1 : 0, {

‎src/components/SegmentedButtons/SegmentedButtonItem.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ const SegmentedButtonItem = ({
163163
testID={testID}
164164
background={background}
165165
hitSlop={hitSlop}
166-
theme={theme}
166+
rippleColor={theme.colors.stateLayerPressed}
167167
style={[
168168
styles.touchable,
169169
borderRadius,

‎src/components/SegmentedButtons/SegmentedButtons.tsx‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,6 @@ const SegmentedButtons = <T extends string = string>({
135135
const theme = useInternalTheme(themeOverrides);
136136
const { direction } = useLocale();
137137

138-
const singleSelectedIndex = multiSelect
139-
? -1
140-
: buttons.findIndex((item) => value === item.value);
141-
142138
return (
143139
<View
144140
role={multiSelect ? 'group' : 'radiogroup'}
@@ -155,7 +151,7 @@ const SegmentedButtons = <T extends string = string>({
155151

156152
const checked = multiSelect
157153
? value.includes(itemValue)
158-
: index === singleSelectedIndex;
154+
: value === itemValue;
159155

160156
const handlePress = (event: GestureResponderEvent) => {
161157
onItemPress?.(event);

‎src/components/__tests__/SegmentedButton.test.tsx‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,31 @@ describe('selected check icon', () => {
12721272
expect(screen.getByTestId('walking-check-icon')).toBeOnTheScreen();
12731273
});
12741274

1275+
it('avoids animation setup for a static option icon', async () => {
1276+
const reanimated = jest.requireMock('react-native-reanimated') as {
1277+
useAnimatedStyle: typeof import('react-native-reanimated').useAnimatedStyle;
1278+
useSharedValue: typeof import('react-native-reanimated').useSharedValue;
1279+
};
1280+
const useAnimatedStyleSpy = jest.spyOn(reanimated, 'useAnimatedStyle');
1281+
const useSharedValueSpy = jest.spyOn(reanimated, 'useSharedValue');
1282+
1283+
try {
1284+
await render(
1285+
<SegmentedButtons
1286+
value="walk"
1287+
buttons={[{ value: 'walk', icon: 'walk' }]}
1288+
onValueChange={() => {}}
1289+
/>
1290+
);
1291+
1292+
expect(useSharedValueSpy).toHaveBeenLastCalledWith(0);
1293+
expect(useAnimatedStyleSpy).not.toHaveBeenCalled();
1294+
} finally {
1295+
useAnimatedStyleSpy.mockRestore();
1296+
useSharedValueSpy.mockRestore();
1297+
}
1298+
});
1299+
12751300
it('restores the option icon and resets its scale when selected checks are disabled', async () => {
12761301
const reanimated = jest.requireMock('react-native-reanimated') as {
12771302
withSpring: typeof import('react-native-reanimated').withSpring;

0 commit comments

Comments
 (0)