Last active
December 18, 2024 15:07
-
-
Save louy/6b66c45ae47bb4e3bac5a104dd0649ff to your computer and use it in GitHub Desktop.
RN Accessibility Wrapper, a custom view that allows you to control the accessibility behaviour of a React Native component tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author Louay Alakkad (github.com/louy) | |
* @license MIT https://opensource.org/licenses/MIT | |
*/ | |
import React from 'react' | |
import PropTypes from 'prop-types' | |
import { | |
NativeModules, | |
ViewProps, | |
ViewPropTypes, | |
findNodeHandle, | |
requireNativeComponent, | |
Platform, | |
View | |
} from 'react-native' | |
const { RNAccessibilityWrapperManager } = NativeModules | |
const RNAccessibilityWrapper = requireNativeComponent( | |
'RNAccessibilityWrapper' | |
) as React.ComponentClass<any> | |
interface AccessibilityWrapperProps extends ViewProps { | |
fieldsRefs?: React.RefObject<React.Component>[] | |
} | |
const AccessibilityWrapperPropTypes = { | |
...ViewPropTypes, | |
fieldsRefs: PropTypes.arrayOf(PropTypes.shape({ | |
current: PropTypes.object | |
}) as PropTypes.Validator<React.RefObject<React.Component>>) | |
} | |
class AccessibilityWrapperIOS extends React.Component< | |
AccessibilityWrapperProps | |
> { | |
public static propTypes = AccessibilityWrapperPropTypes | |
private ref = React.createRef<React.Component<any>>() | |
public componentDidMount() { | |
if (this.props.fieldsRefs) { | |
this.setAccessibilityFields(this.props.fieldsRefs.map(ref => ref.current)) | |
} | |
} | |
public componentDidUpdate() { | |
if (this.props.fieldsRefs) { | |
this.setAccessibilityFields(this.props.fieldsRefs.map(ref => ref.current)) | |
} | |
} | |
private setAccessibilityFields = ( | |
fields: (React.Component<any> | null)[] | |
) => { | |
const fieldTags = | |
fields && fields.map(component => component && findNodeHandle(component)) | |
return RNAccessibilityWrapperManager.setAccessibilityFields( | |
findNodeHandle(this.ref.current), | |
fieldTags | |
) | |
} | |
public render() { | |
return <RNAccessibilityWrapper {...this.props} ref={this.ref} /> | |
} | |
} | |
const AccessibilityWrapperAndroid: React.FunctionComponent< | |
AccessibilityWrapperProps | |
> = ({ fieldsRefs, ...props }) => <View {...props} /> | |
AccessibilityWrapperAndroid.propTypes = AccessibilityWrapperPropTypes | |
/** | |
* The AccessibilityWrapper component allows you to adjust the behaviour of the native platform | |
* when it comes to accessibility. Using this component you can tell the native platform to | |
* group all subviews together for accessibility purposes (since it's not always done by | |
* default), and you can even override the focus order of subviews | |
* | |
* @example | |
* export default class App extends Component<{}> { | |
* fooRef = React.createRef<Text>(); | |
* barRef = React.createRef<Text>(); | |
* bazRef = React.createRef<Text>(); | |
* | |
* public render() { | |
* return ( | |
* <AccessibilityWrapper fieldsRefs={[ | |
* this.barRef, | |
* this.fooRef, | |
* this.bazRef, | |
* ]}> | |
* <SafeAreaView> | |
* <Text ref={this.fooRef}>Foo</Text> | |
* <Text ref={this.barRef}>Bar</Text> | |
* <Text ref={this.bazRef}>Baz</Text> | |
* </SafeAreaView> | |
* </AccessibilityWrapper> | |
* ); | |
* } | |
* } | |
*/ | |
export default Platform.select<React.ComponentType<AccessibilityWrapperProps>>({ | |
ios: AccessibilityWrapperIOS as React.ComponentType< | |
AccessibilityWrapperProps | |
>, | |
android: AccessibilityWrapperAndroid as React.ComponentType< | |
AccessibilityWrapperProps | |
> | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RNAccessibilityWrapper.h | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <UIKit/UIKit.h> | |
#import <UIKit/UIAccessibilityContainer.h> | |
#import <React/RCTView.h> | |
@interface RNAccessibilityWrapper : RCTView | |
- (void) setAccessibilityFields: (NSArray *)reactTags; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RNAccessibilityWrapper.m | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <Foundation/Foundation.h> | |
#import "RNAccessibilityWrapper.h" | |
#import <UIKit/UIKit.h> | |
@implementation RNAccessibilityWrapper | |
- (void) setAccessibilityFields: (NSArray *)fields | |
{ | |
NSMutableArray *accessibleElements = [NSMutableArray arrayWithCapacity:[fields count]]; | |
[fields enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { | |
UIView *field = obj; | |
[accessibleElements addObject:field]; | |
}]; | |
self.accessibilityElements = (NSArray *)accessibleElements; | |
} | |
- (bool) shouldGroupAccessibilityChildren { | |
return YES; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RNAccessibilityViewManager | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <React/RCTViewManager.h> | |
@interface RNAccessibilityWrapperManager : RCTViewManager | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RNAccessibilityWrapper.m | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <Foundation/Foundation.h> | |
#import <React/RCTUIManager.h> | |
#import "RNAccessibilityWrapper.h" | |
#import "RNAccessibilityWrapperManager.h" | |
@implementation RNAccessibilityWrapperManager | |
RCT_EXPORT_MODULE() | |
- (UIView *)view { | |
return [[RNAccessibilityWrapper alloc] init]; | |
} | |
RCT_EXPORT_METHOD(setAccessibilityFields:(nonnull NSNumber *)reactTag | |
fieldsReactTags: (nonnull NSArray *)fieldsReactTags) { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
RNAccessibilityWrapper *component = (RNAccessibilityWrapper *)[self.bridge.uiManager viewForReactTag:reactTag]; | |
NSMutableArray *fields = [NSMutableArray arrayWithCapacity:[fieldsReactTags count]]; | |
[fieldsReactTags enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { | |
NSNumber *tag = (NSNumber *)obj; | |
UIView *field = [self.bridge.uiManager viewForReactTag:tag]; | |
[fields addObject:field]; | |
}]; | |
[component setAccessibilityFields: fields]; | |
}); | |
} | |
@end |
@ArturKalach Nice work! Will definitely implement it in my projects. Have needed this for a long time.
@ArturKalach Nice work! Will definitely implement it in my projects. Have needed this for a long time.
@AdamGerthel
Thanks a lot! I spent weeks finishing this, and your words show that it wasn't a waste of time)
The native and new architecture are really tricky, but it's been a good experience. There is still some work to be done, but the API is complete. There's still a lot of work left, but I really enjoy the results.
P.S. I think this page and the work of @louy were a starting point.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello everyone,
I have finished working on an "advanced" ordering system for React Native:
It has some nuances, but honestly, it works really well!
API is really simple, we could control the order by web way via indexes.
NO ScrollView grouping
Additionally, there's no need to group content using
ScrollView
for horizontal scrolls anymore.A11y.Group
functions almost like a bare View, but, it achieves the same effect