-
-
Save louy/6b66c45ae47bb4e3bac5a104dd0649ff to your computer and use it in GitHub Desktop.
/** | |
* @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 | |
> | |
}) |
// | |
// 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 |
// | |
// 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 |
// | |
// RNAccessibilityViewManager | |
// | |
// Created by Louay Alakkad on 10/04/2019. | |
// License: MIT https://opensource.org/licenses/MIT | |
// | |
#import <React/RCTViewManager.h> | |
@interface RNAccessibilityWrapperManager : RCTViewManager | |
@end |
// | |
// 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 |
Hi, tnx for your work man. Probably it's why I can fix bugs. I wrote a lib, it can be usefull I guess https://www.npmjs.com/package/react-native-a11y (NewArch, Android, iOs ) supported
Nice @ArturKalach !! Will check this out. Focus order has long been a pain point with RN and some layouts.
Hello everyone,
I have finished working on an "advanced" ordering system for React Native:
- https://github.com/ArturKalach/react-native-a11y-order
- https://www.npmjs.com/package/react-native-a11y-order
Ordering | Grouping |
---|---|
|
|
It has some nuances, but honestly, it works really well!
API is really simple, we could control the order by web way via indexes.
<A11y.Order>
<A11y.Index index={1}>
<Text style={styles.font}>
First
</Text>
</A11y.Index>
<A11y.Index index={3}>
<Text style={styles.font}>
Third
</Text>
</A11y.Index>
<A11y.Index index={2}>
<Text style={styles.font}>
Second
</Text>
</A11y.Index>
</A11y.Order>
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
@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.
Hi, tnx for your work man. Probably it's why I can fix bugs.
I wrote a lib, it can be usefull I guess https://www.npmjs.com/package/react-native-a11y (NewArch, Android, iOs ) supported