Skip to content

Conversation

@eulphean
Copy link
Contributor

This change exposes a way to set accessibility focus on RX.TextInput. This also changes the way we had AccessibilityUtil implementations individually for each platform. That becomes an overhead for each component that wants to leverage those platform helpers. We need to create a platform specific copy of each component and leverage the platform specific AccessibilityUtil.

With this change, we let Native-Common AccessibilityUtil do all the work for calling any platform specific methods.

@msftclas
Copy link

@eulphean,
Thanks for your contribution as a Microsoft full-time employee or intern. You do not need to sign a CLA.
Thanks,
Microsoft Pull Request Bot


export class AccessibilityUtil extends CommonAccessibilityUtil {
// Native platform specific instance for AccessibilityUtil.
private _instance: typeof AndroidAccessibilityUtil | typeof iOSAccessibilityUtil;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should both be the same type. You should define an interface (or abstract base class) that both iOS and Android need to implmenet

break;

default:
assert(false, 'Unknown platform.');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uwp isn't an unknown platform inside ReactXP

};

// Platform specific helpers exposed through Native-Common AccessibilityUtil.
export abstract class NativeHelpers {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider giving a more specific name to this class to be derived by each platform. Maybe someone like CommonNativeAccessibilityUtil which would be in line with the pattern used currently or NativeAccessibilityHelpers. NativeHelpers is very generic and makes it's difficult to the reader to understand what it's about on its own.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Seb. NativeHelpers is too generic a name to export.


import Accessibility from '../native-common/Accessibility';
import { AccessibilityUtil as CommonAccessibilityUtil } from '../native-common/AccessibilityUtil';
import { NativeHelpers } from '../native-common/AccessibilityUtil';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have a potential circular dependency here. native-common/AccessibilityUtil -> android/AccessibilityUtil -> native-common/AccessibilityUtil

@@ -0,0 +1,22 @@
/**
* iOSAccessibilityUtil.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should match the file name.

import RN = require('react-native');

import { AccessibilityUtil as CommonAccessibilityUtil } from '../common/AccessibilityUtil';
import AndroidAccessibilityUtil from '../android/AccessibilityUtil';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you importing platform-specific files within a common file? This is an anti-pattern and will create dependency cycles. Platform-specific files should import native-common, not the other way around.

Copy link
Collaborator

@berickson1 berickson1 Apr 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is an anti-pattern, it's beneficial to us. The idea behind this is that pretty much every UI component needs to talk to AccessibilityUtils in some way, shape or form. That leaves a few options:

  1. Extend every UI component (that needs accessibility) for every platform so that it imports the platform specific AccessibiilityUtils and calls the function
  2. Provide some sort of override/shim at the RX platform creation (something AccessibilityUtils.setPlatformHelperFunctions that pipes to this._impl that's in this PR)
  3. Have native-common/AccessibilityUtils decide which impl to use on init and import platform specific implementations

#1 adds a bunch of extra code and another layer of abstract classes
#2 is somewhat obscure and non-obvious to someone working in the codebase
#3 isn't ideal but seems somewhat better than the other options. (Also can be done without cycles)

Do you have any alternative solutions?

};

// Platform specific helpers exposed through Native-Common AccessibilityUtil.
export abstract class NativeHelpers {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Seb. NativeHelpers is too generic a name to export.

Amay Kataria added 3 commits April 26, 2017 15:28
… components can leverage native-common instance directly to set focus on iOS and Android platforms.
@eulphean
Copy link
Contributor Author

@erictraut @berickson1

I have a new iteration addressing the comments.

@@ -1,5 +1,5 @@
/**
* AccessibilityUtil.ts
* AndroidAccessibilityUtil.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you rename this file to start with "Android"? It's already in the Android directory, so adding this is redundant. It's also inconsistent with all of the other code in ReactXP. I'd prefer if you went back to the previous name for consistency.

@@ -0,0 +1,22 @@
/**
* iOSAccessibilityUtil.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again, the file name shouldn't contain iOS since that's redundant. It's already in a folder called iOS. Same with the class name it exports.

import RN = require('react-native');

import { AccessibilityUtil as CommonAccessibilityUtil, PlatformAccessibilityHelpers } from '../common/AccessibilityUtil';
import AndroidAccessibilityUtil from '../android/AndroidAccessibilityUtil';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still inverted. The "native common" implementation should not be importing the platform-specific implementations. It should be the other way around. The platform-specific implementations should be importing the common and overriding the parts that are necessary. Doing it the other way is inconsistent with all other parts of ReactXP and has the potential for creating dependency cycles.

// Set AccessibilityUtil instance based on the platform.
switch (RN.Platform.OS) {
case 'android':
this._instance = AndroidAccessibilityUtil;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instantiation of platform-specific modules should happen only in the ReactXP.ts files that are specific to each platform. You shouldn't be doing it here in this native-common module. By doing so, you're pulling in all platform implementation on all platforms. The iOS version of ReactXP shouldn't contain any Android-specific code and vice versa. By instantiating the platform-specific code in ReactXP.ts, we are able to strip out the code for other platforms. By doing it here, you defeat that mechanism.

@@ -0,0 +1,20 @@
/**
* WindowsAccessibilityUtil.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See note above. This file and its exported class shouldn't start with "Windows".

@eulphean
Copy link
Contributor Author

Thanks for the feedback, guys. I just pushed a latest iteration that should address the concerns around this. The problem with using a flat inheritance model how RX.Linking component does is that platform specific implementations need to be initialized inside Native-Common AccessibilityUtil in order for it to consume it. This brings up the concern of importing platform specific code into Native-Common instance that defeats the purpose of ReactXp's message.

By letting initialization path in ReactXp poke the platform specific instance inside native-common AccessibilityUtil, we solve that problem.

I used Seb's suggestion for naming the platform specific accessibility helper. Goal was to keep the suffix 'Util' to bear resemblance and use the term 'Native' to drive the difference between the two classes.

@erictraut
Copy link
Contributor

Looking better. I realize this approach is still a bit convoluted, but I think it's much better than the previous approach. Once @berickson1 reviews and approves the change, he or I can merge it in.

…ame AccessibilityNativeUtil to AccessibilityPlatformUtil as that is more in sync with all the comments in the codebase.
@berickson1 berickson1 merged commit cff4b51 into microsoft:master Apr 27, 2017
@eulphean eulphean deleted the ak/a11yTextInput branch May 3, 2017 05:37
berickson1 pushed a commit to berickson1/reactxp that referenced this pull request Oct 22, 2018
… expose setting accessibility focus on RX.TextInput. (microsoft#69)

* Expose setAccessibilityFocus from Native-Common AccessibilityUtil, so components can leverage native-common instance directly to set focus on iOS and Android platforms.

* Code review feedback.

* PR feedback.

* PR feedback.

* Nit: Space

* PR feedback. Rename platform specific files to AccessibilityUtil. Rename AccessibilityNativeUtil to AccessibilityPlatformUtil as that is more in sync with all the comments in the codebase.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants