diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc97e6cc..4b115061 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18] + node-version: [20] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18] + node-version: [20] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18] + node-version: [20] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 @@ -89,7 +89,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18] + node-version: [20] java-version: [11] steps: - uses: actions/checkout@v2 @@ -123,7 +123,7 @@ jobs: runs-on: macos-latest strategy: matrix: - node-version: [18] + node-version: [20] steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/ios/RNCSegmentedControl.h b/ios/RNCSegmentedControl.h index af81a54a..5516e946 100644 --- a/ios/RNCSegmentedControl.h +++ b/ios/RNCSegmentedControl.h @@ -11,5 +11,6 @@ @interface RNCSegmentedControl : UISegmentedControl @property(nonatomic, assign) NSInteger selectedIndex; @property(nonatomic, copy) RCTBubblingEventBlock onChange; +@property(nonatomic, copy) NSArray *testIDS; @end diff --git a/ios/RNCSegmentedControl.m b/ios/RNCSegmentedControl.m index 11368cf1..4a1bfea4 100644 --- a/ios/RNCSegmentedControl.m +++ b/ios/RNCSegmentedControl.m @@ -88,4 +88,17 @@ - (void)setAppearance:(NSString *)appearanceString { #endif } +- (NSArray *)accessibilityElements { + NSArray *elements = [super accessibilityElements]; + [elements enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) { + @try { + obj.accessibilityIdentifier = self.testIDS[idx]; + } @catch (NSException *exception) { + NSLog(@"%@", exception); + } + }]; + + return elements; +} + @end diff --git a/ios/RNCSegmentedControlManager.m b/ios/RNCSegmentedControlManager.m index 6a277958..553bc1f3 100644 --- a/ios/RNCSegmentedControlManager.m +++ b/ios/RNCSegmentedControlManager.m @@ -27,6 +27,7 @@ - (UIView *)view { RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) RCT_EXPORT_VIEW_PROPERTY(appearance, NSString) +RCT_EXPORT_VIEW_PROPERTY(testIDS, NSArray) RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSObject, RNCSegmentedControl) { #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \ diff --git a/js/SegmentedControl.js b/js/SegmentedControl.js index 99bdc2eb..903f2784 100644 --- a/js/SegmentedControl.js +++ b/js/SegmentedControl.js @@ -37,11 +37,13 @@ const SegmentedControl = ({ activeFontStyle, appearance, accessibilityHintSeperator = 'out of', + testIDS, }: SegmentedControlProps): React.Node => { const colorSchemeHook = useColorScheme(); const colorScheme = appearance || colorSchemeHook; const [segmentWidth, setSegmentWidth] = React.useState(0); const animation = React.useRef(new Animated.Value(0)).current; + const ref = React.useRef(); const handleChange = (index: number) => { // mocks iOS's nativeEvent @@ -55,6 +57,17 @@ const SegmentedControl = ({ onValueChange && onValueChange(values[index]); }; + const updateSegmentWidth = React.useCallback( + (width: number) => { + const newSegmentWidth = values.length ? width / values.length : 0; + if (newSegmentWidth !== segmentWidth) { + animation.setValue(newSegmentWidth * (selectedIndex || 0)); + setSegmentWidth(newSegmentWidth); + } + }, + [values.length, segmentWidth, animation, selectedIndex], + ); + React.useEffect(() => { if (animation && segmentWidth) { let isRTL = I18nManager.isRTL ? -segmentWidth : segmentWidth; @@ -67,8 +80,15 @@ const SegmentedControl = ({ } }, [animation, segmentWidth, selectedIndex]); + React.useEffect(() => { + if (ref.current) { + ref.current.measure((_x, _y, width) => updateSegmentWidth(width)); + } + }, [values]); + return ( { - const newSegmentWidth = values.length ? width / values.length : 0; - if (newSegmentWidth !== segmentWidth) { - animation.setValue(newSegmentWidth * (selectedIndex || 0)); - setSegmentWidth(newSegmentWidth); - } - }}> + }) => updateSegmentWidth(width)}> {!backgroundColor && !tintColor && ( index ? testIDS[index] : `${index}` + } selected={selectedIndex === index} accessibilityHint={`${ index + 1 diff --git a/js/SegmentedControlTab.js b/js/SegmentedControlTab.js index fcf4971e..db2ca259 100644 --- a/js/SegmentedControlTab.js +++ b/js/SegmentedControlTab.js @@ -94,8 +94,9 @@ export const SegmentedControlTab = ({ onPress={onSelect} accessibilityHint={accessibilityHint} accessibilityRole="button" - accessibilityState={{selected: selected, disabled: !enabled}}> - + accessibilityState={{selected: selected, disabled: !enabled}} + testID={testID}> + {typeof value === 'number' || typeof value === 'object' ? ( ) : isBase64(value) ? ( diff --git a/js/types.js b/js/types.js index 3eb54fc5..4c96c1ef 100644 --- a/js/types.js +++ b/js/types.js @@ -107,4 +107,9 @@ export type SegmentedControlProps = $ReadOnly<{| * Touchable style properties for Segmented Control Tab */ tabStyle?: ViewStyle, + + /** + * array testID to each segment button + */ + testIDS: $ReadOnlyArray, |}>; diff --git a/package.json b/package.json index 705d484b..6aa7dd5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@react-native-segmented-control/segmented-control", - "version": "2.5.4", + "version": "2.5.5", "description": "React Native SegmentedControlIOS library", "main": "js/index.js", "types": "index.d.ts",