feat(core): a11y support#8909
Conversation
|
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign the CLA at https://www.nativescript.org/cla. |
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
This comment was marked as abuse.
This comment was marked as abuse.
I can but it won't be exactly the same as the current behavior.
Currently Hmm... I should probably rename |
@m-abs I think for android, we can do something like this: const id = Utils.android.resources.getId(':id/nativescript_accessibility_id')
this.nativeViewProtected.setTag(id, value)
this.nativeViewProtected.setTag(value)Tools like wix/Detox use that when getting views by id on android, and I don't 100% recall why I had to set the tag with the id and without, I took that from a sample app I made with detox: In my case I named the property |
|
Thanks for working that out. 👍 I'll add it right away. |
|
A few comments. The accessibility/index.android.ts is not snapshot compatible. It accesses native namespace at its root which will fail in snapshot generation. |
This comment was marked as abuse.
This comment was marked as abuse.
We initial made this as a trade off. I've considered binding the |
Does this mean, I can ship the |
|
Please I know you deprecate this but people still use it ! If this PRs gets in as it is it will break snapshots. I would gladly update the code to make it snapshot compatible. It does not represent much work. As an example here all namespace / classes you added at the top of the file for easy access means they are marshalled (which can be dead slow, look at android R namespace as an example) even though they might never be used. @NathanaelA I meant I would like to know the consequence of that PR (perfs, men, but mostly perfs) on an app which actually does not "use" or implement accessibility. |
|
@farfromrefug |
|
@m-abs thanks a lot it is perfect now! |
|
I think, I found a better way to get from the I use the |
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
This comment was marked as abuse.
facetious
left a comment
There was a problem hiding this comment.
There are a few files that are misspelled as acessibility with one c; most notably the folder that they all live within.
| * @param {boolean} receivedFocus | ||
| * @param {boolean} lostFocus | ||
| */ | ||
| export function notifyAccessibilityFocusState(view: View, receivedFocus: boolean, lostFocus: boolean): void { |
There was a problem hiding this comment.
One boolean would be sufficient.
This method is only used by iOS, but is in a Apologies; the android file was collapsed by default due to its length.-common. Is that intentional?
|
|
||
| const oldActiveFontScaleCategory = currentFontScaleCategory; | ||
| if (global.isAndroid) { | ||
| currentFontScaleCategory = fontExtraMediumClass; |
There was a problem hiding this comment.
I think it would be better to define these font classes as identical on Android, rather than remove this functionality. It could be useful for developers to be able to target specific font size css classes in their on cascades. Unless I'm misunderstanding the reasoning behind this neutralizing?
There was a problem hiding this comment.
Unfortunately there is a huge difference between the font-size the user can set on the two platforms, I'll try to explain:
Android:
The font-size is an integer between 0.85 and 1.3 (with some rounding errors).
iOS:
We have the setting preferredContentSizeCategory that maps to a global string constant, they are mapped here:
NativeScript/packages/core/acessibility/fontscale-observable.ios.ts
Lines 14 to 27 in 6b7cbe6
Range 0.5, 0.7, 0.85, 1, 1.15, 1.3, 1.5, 2, 2.5, 3, 3.5, 4
Values <0.85 we call extra small
Values >1.5 we call extra small
The (almost) shared range on iOS and Android 0.85 to 1.5 we call medium size.
We introduced this, because large increases in the font-sizes like 400% breaks most layouts.
I hope that makes sense.
|
|
||
| export const AccessibilityServiceEnabledPropName = 'accessibilityServiceEnabled'; | ||
|
|
||
| export class CommonA11YServiceEnabledObservable extends SharedA11YObservable { |
There was a problem hiding this comment.
I've noticed a number of places where you've used A11Y where I would have normally expected A11y. I'm open to discussion here, but my preference is the lowercase y.
There was a problem hiding this comment.
I'm considering just writing it out as Accessibility :)
| [touchExplorationStateEnabledPropName]: boolean; | ||
|
|
||
| get accessibilityServiceEnabled(): boolean { | ||
| return !!this.accessibilityServiceEnabled && !!this.touchExplorationStateEnabled; |
There was a problem hiding this comment.
Looks like an infinite recursion to me. Did you mean to use accessibilityStateEnabled instead of accessibilityServiceEnabled?
There was a problem hiding this comment.
Thanks, that is clearly an error.
| /** | ||
| * Get the Android platform's AccessibilityManager. | ||
| */ | ||
| export function getAndroidAccessibilityManager(): android.view.accessibility.AccessibilityManager | null; |
There was a problem hiding this comment.
Android-specific types should be left out of common .d.ts files. In the remainder of the repo, this is done with : any /* <class name> */.
| } | ||
|
|
||
| public androidSendAccessibilityEvent(eventName: AndroidAccessibilityEvent, msg?: string): void { | ||
| if (!isAccessibilityServiceEnabled()) { |
There was a problem hiding this comment.
Checking the value here (and in the following two functions) seems superfluous to me. The final destination of all three ends up within sendAccessibilityEvent, which checks this condition anyway.
| /** | ||
| * Internal use only. This is used to limit the number of updates to android.view.View.setContentDescription() | ||
| */ | ||
| _androidContentDescriptionUpdated?: boolean; |
There was a problem hiding this comment.
If it's internal use only, it could be left out of the .d.ts and just locally cast in order for TypeScript to continue validating the type.
There was a problem hiding this comment.
I get a typing error in packages/core/acessibility/index.android.ts if I remove it.
| * type = 'layout' used when the layout of a screen changes. | ||
| * type = 'screen' large change made to the screen. | ||
| */ | ||
| public iosPostAccessibilityNotification(notificationType: IOSPostAccessibilityNotificationType, msg?: string): void; |
There was a problem hiding this comment.
I think these two methods would be best combined together.
interface AccessibilityEventOptions {
androidAccessibilityEvent: AndroidAccessibilityEvent;
iosNotificationType: IOSPostAccessibilityNotificationType;
message: string;
}
public sendAccessibilityEvent(options: Partial<AccessibilityEventOptions>): void;| /** | ||
| * Announce screen changed | ||
| */ | ||
| public accessibilityScreenChanged(refocus?: boolean): void; |
There was a problem hiding this comment.
I think you mean for this to be within the //@endprivate. It should also follow the pattern: onAccessibilityScreenChanged.
There was a problem hiding this comment.
The developer should be able to call this function for large updates on the page.
| public enableSwipeBackNavigation: boolean; | ||
| public backgroundSpanUnderStatusBar: boolean; | ||
| public hasActionBar: boolean; | ||
| public accessibilityAnnouncePageEnabled = true; |
There was a problem hiding this comment.
This property should mirror the definition of the other boolean properties, including specifying the default value elsewhere.
There was a problem hiding this comment.
I'm not sure I understand.
This comment has been minimized.
This comment has been minimized.
|
Hi @NathanaelA Sorry your reply got lost in all the other replies.
It is set here: https://github.com/m-abs/NativeScript/blob/feat/accessibility/packages/core/accessibility/index.android.ts#L563-L667 I attempt to recreate the behavior from iOS by concatenating
We definitely need to have Appium working with this, but my experience with it is very limited. @rigor789 Do you have some input here? Can |
885f354 to
d4fce71
Compare
|
Hi on android, we used to benefit from using automationText prop for reference - look at the NS7 code, where setContentDescription is used: and in NS8 it looks now like this: (probably for solving the issue for Detox platform): please notice that in NS8, automationText now points to accessibilityIdentifierProperty: to summerise: |
|
hi @vigdora, I'm sorry this causes you problems.
No, that would make the app inaccessible for accessibility users on android and defeat the purpose of this PR. The old implementation was wrong, Android's Unfortunately you have to update your tests to make them work with this change. I haven't worked with Using |
|
@m-abs The link you referred to doesn't help much since it uses Detox or still uses the Accessibility ID with Appium. |
|
@vigdora there is a quick workaround. Create a plugin which adds a mixin N. In that mixin override the |
|
@farfromrefug @m-abs |
|
@farfromrefug and @vigdora You should probably create a new issue about it and maybe take the discussion on slack. |







PR Checklist
What is the current behavior?
NativeScript doesn't support native accessibility features without our plugin
@nota/nativescript-accessibility-extUnfortunately that plugin will soon go unmaintained.
I'd hate for the community to loose this feature, so here I'll contribute the feature to upstream NativeScript.
What is the new behavior?
View-class.etc.
TODO:
iOSApplication.accessibilityMagicTapEventwhich users can wire into.accessibilityPerformEscapehttps://developer.apple.com/documentation/objectivec/nsobject/1615091-accessibilityperformescape). Allow default to dismiss the active dialog however also emit a newApplication.accessibilityPerformEscapeEventwhich allows an override for custom behavior - most likely viaargs.cancel = truefrom the event emission.Design document here:
https://docs.google.com/document/d/1gybrT1IQh8Fb5r0v_tZTMS06yP6xYmr9pDQIBRbokZc/edit
Implements #6993
Fixes: #6125
BREAKING CHANGES:
Remove the property
automationTextas it conflicts with the new propertyaccessibilityIdthe implements the behavior correctly.[Describe the impact of the changes here.]
Migration steps:
[Provide a migration path for existing applications.]