Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions apps/toolbox/src/pages/a11y.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,33 @@
<GridLayout padding="20" class="a11y-demo-page">
<ScrollView>
<StackLayout>
<Button text="Open Modal Page" class="view-item" tap="{{openModal}}" />
<Button text="Open Normal Page" class="view-item" tap="{{openNormal}}" />
<Button testID="openModalPageButton" text="Open Modal Page" class="view-item" tap="{{openModal}}" />
<Button testID="openNormalPageButton" text="Open Normal Page" class="view-item" tap="{{openNormal}}" />

<Label text="Accessible Label" class="view-item a11y text-center" accessibilityLabel="Accessible Label" accessibilityHint="Just a label" accessibilityRole="{{accessibilityRole.StaticText}}" accessibilityValue="Accessible Label" />
<Button text="Accessible Button" class="view-item a11y" accessibilityLabel="Accessible Button" accessibilityHint="Tapping this really does nothing" />
<Label testID="testLabel1" text="Accessible Label" class="view-item a11y text-center" accessibilityLabel="Accessible Label" accessibilityHint="Just a label" accessibilityRole="{{accessibilityRole.StaticText}}" accessibilityValue="Accessible Label" />
<Button testID="testLabel2" text="Accessible Button" class="view-item a11y" accessibilityLabel="Accessible Button" accessibilityHint="Tapping this really does nothing" />

<Image src="res://icon" width="50" class="view-item a11y" accessibilityLabel="Image with explicit attribute role" accessibilityRole="{{accessibilityRole.Image}}" />
<Image src="res://icon" width="50" class="view-item a11y a11y-role-image" accessibilityLabel="Image with css defined role" />
<Image testID="testImage1" src="res://icon" width="50" class="view-item a11y" accessibilityLabel="Image with explicit attribute role" accessibilityRole="{{accessibilityRole.Image}}" />
<Image testID="testImage2" src="res://icon" width="50" class="view-item a11y a11y-role-image" accessibilityLabel="Image with css defined role" />


<Image src="{{ largeImageSrc }}" width="50" class="view-item a11y a11y-role-image" accessibilityLabel="Image with css defined role" />

<Switch checked="true" class="view-item a11y" accessibilityLabel="Switch with attribute state" accessibilityState="{{accessibilityState.Checked}}" checkedChange="{{checkedChange}}" />
<Switch checked="true" class="view-item a11y a11y-state-checked" accessibilityLabel="Switch with css state" checkedChange="{{checkedChange}}" />
<Switch testID="testSwitch1" checked="true" class="view-item a11y" accessibilityLabel="Switch with attribute state" accessibilityState="{{accessibilityState.Checked}}" checkedChange="{{checkedChange}}" />
<Switch testID="testSwitch2" checked="true" class="view-item a11y a11y-state-checked" accessibilityLabel="Switch with css state" checkedChange="{{checkedChange}}" />

<TextView hint="TextView" text="{{switchCheckedText}}" class="view-item a11y" accessibilityLabel="TestView with a value" accessibilityLiveRegion="{{accessibilityLiveRegions.Polite}}" />
<TextField hint="TextField" class="view-item a11y" accessibilityLabel="Plain jane TextField" accessibilityHint="Tell us your real name Jane" />
<TextView testID="testTextView" hint="TextView" text="{{switchCheckedText}}" class="view-item a11y" accessibilityLabel="TestView with a value" accessibilityLiveRegion="{{accessibilityLiveRegions.Polite}}" />
<TextField testID="testTextField" hint="TextField" class="view-item a11y" accessibilityLabel="Plain jane TextField" accessibilityHint="Tell us your real name Jane" />
<TextView hint="TextView" class="view-item a11y" accessibilityLabel="Nice TextView" accessibilityHint="Tell us about yourself Jane" />
<GridLayout rows="25" columns="*" class="view-item" accessibilityLabel="No can go GridLayout" accessibilityHint="A grid that will not get bigger when increasing accessible text size">
<GridLayout testID="testGridLayout1" rows="25" columns="*" class="view-item" accessibilityLabel="No can go GridLayout" accessibilityHint="A grid that will not get bigger when increasing accessible text size">
<Label text="IN-Accessible Grid" class="view-item text-center" />
</GridLayout>
<GridLayout rows="25,25" columns="*,50" class="view-item a11y" accessibilityLabel="Yes an accessible GridLayout" accessibilityHint="A grid that WILL get bigger dynamically when increasing accessible text size">
<Label text="Accessible Grid" class="view-item text-center" />
<Label row="1" text="With another item in a row" class="view-item text-center" />
<Label rowSpan="2" col="1" text="Hi" />
</GridLayout>
<Slider value="10" minValue="0" maxValue="100" class="view-item a11y" accessibilityLabel="Slider" accessibilityHint="A smooth slider" accessibilityValue="10" />
<Slider testID="testSlider" value="10" minValue="0" maxValue="100" class="view-item a11y" accessibilityLabel="Slider" accessibilityHint="A smooth slider" accessibilityValue="10" />
</StackLayout>
</ScrollView>
</GridLayout>
Expand Down
5 changes: 5 additions & 0 deletions packages/core/accessibility/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,11 @@ function applyContentDescription(view: Partial<View>, forceUpdate?: boolean) {

const contentDescription = contentDescriptionBuilder.join('. ').trim().replace(/^\.$/, '');

if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && view.testID) {
// ignore when testID is enabled
return;
}

if (contentDescription) {
if (Trace.isEnabled()) {
Trace.write(`${cls} - set to "${contentDescription}"`, Trace.categories.Accessibility);
Expand Down
1 change: 1 addition & 0 deletions packages/core/global-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ declare const __CSS_PARSER__: string;
declare const __NS_WEBPACK__: boolean;
declare const __UI_USE_EXTERNAL_RENDERER__: boolean;
declare const __UI_USE_XML_PARSER__: boolean;
declare const __USE_TEST_ID__: boolean | undefined;
declare const __ANDROID__: boolean;
declare const __IOS__: boolean;

Expand Down
4 changes: 4 additions & 0 deletions packages/core/platforms/android/res/values/ids.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="nativescript_accessibility_id"/>
</resources>
31 changes: 26 additions & 5 deletions packages/core/ui/core/view/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Point, CustomLayoutView as CustomLayoutViewDefinition } from '.';
import type { GestureTypes, GestureEventData } from '../../gestures';

// Types.
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties';
import { layout } from '../../../utils';
import { Trace } from '../../../trace';
Expand Down Expand Up @@ -796,18 +796,39 @@ export class View extends ViewCommon {
this.nativeViewProtected.setAlpha(float(value));
}

[testIDProperty.setNative](value: string) {
this.setTestID(this.nativeViewProtected, value);
}

setTestID(view, value) {
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__) {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');

if (id) {
view.setTag(id, value);
view.setTag(value);
}

view.setContentDescription(value);
}
}

[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.setFocusable(!!value);

updateAccessibilityProperties(this);
}

[accessibilityIdentifierProperty.setNative](value: string): void {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID;
} else {
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');

if (id) {
this.nativeViewProtected.setTag(id, value);
this.nativeViewProtected.setTag(value);
if (id) {
this.nativeViewProtected.setTag(id, value);
this.nativeViewProtected.setTag(value);
}
}
}

Expand Down
19 changes: 17 additions & 2 deletions packages/core/ui/core/view/index.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Point, View as ViewDefinition } from '.';

// Requires
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common';
import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
import { ShowModalOptions, hiddenProperty } from '../view-base';
import { Trace } from '../../../trace';
import { layout, iOSNativeHelper } from '../../../utils';
Expand Down Expand Up @@ -572,6 +572,16 @@ export class View extends ViewCommon implements ViewDefinition {
this.updateOriginPoint(this.originX, value);
}

[testIDProperty.setNative](value: string) {
this.setTestID(this.nativeViewProtected, value);
}

public setTestID(view: any, value: string): void {
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__) {
view.accessibilityIdentifier = value;
}
}

[accessibilityEnabledProperty.setNative](value: boolean): void {
this.nativeViewProtected.isAccessibilityElement = !!value;

Expand All @@ -581,8 +591,13 @@ export class View extends ViewCommon implements ViewDefinition {
[accessibilityIdentifierProperty.getDefault](): string {
return this.nativeViewProtected.accessibilityLabel;
}

[accessibilityIdentifierProperty.setNative](value: string): void {
this.nativeViewProtected.accessibilityIdentifier = value;
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID
} else {
this.nativeViewProtected.accessibilityIdentifier = value;
}
}

[accessibilityRoleProperty.setNative](value: AccessibilityRole): void {
Expand Down
11 changes: 11 additions & 0 deletions packages/core/ui/core/view/view-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public accessibilityValue: string;
public accessibilityHint: string;

public testID: string;

public touchAnimation: boolean | TouchAnimationOptions;
public ignoreTouchAnimation: boolean;

Expand Down Expand Up @@ -1119,6 +1121,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public accessibilityScreenChanged(): void {
return;
}

public setTestID(view: any, value: string) {
return;
}
}

export const originXProperty = new Property<ViewCommon, number>({
Expand Down Expand Up @@ -1196,6 +1202,11 @@ const ignoreTouchAnimationProperty = new Property<ViewCommon, boolean>({
});
ignoreTouchAnimationProperty.register(ViewCommon);

export const testIDProperty = new Property<ViewCommon, string>({
name: 'testID',
});
testIDProperty.register(ViewCommon);

accessibilityIdentifierProperty.register(ViewCommon);
accessibilityLabelProperty.register(ViewCommon);
accessibilityValueProperty.register(ViewCommon);
Expand Down
19 changes: 14 additions & 5 deletions packages/core/ui/text-base/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { layout } from '../../utils';
import { isString, isNullOrUndefined } from '../../utils/types';
import { accessibilityIdentifierProperty } from '../../accessibility/accessibility-properties';
import * as Utils from '../../utils';
import { testIDProperty } from '../../ui/core/view';

export * from './text-base-common';

Expand Down Expand Up @@ -443,13 +444,21 @@ export class TextBase extends TextBaseCommon {
org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0));
}

[testIDProperty.setNative](value: string): void {
this.setTestID(this.nativeTextViewProtected, value);
}

[accessibilityIdentifierProperty.setNative](value: string): void {
// we override the default setter to apply it on nativeTextViewProtected
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (typeof __USE_TEST_ID__ !== 'undefined' && __USE_TEST_ID__ && this.testID) {
// ignore when using testID;
} else {
// we override the default setter to apply it on nativeTextViewProtected
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');

if (id) {
this.nativeTextViewProtected.setTag(id, value);
this.nativeTextViewProtected.setTag(value);
if (id) {
this.nativeTextViewProtected.setTag(id, value);
this.nativeTextViewProtected.setTag(value);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ exports[`angular configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -774,7 +775,8 @@ exports[`angular configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ exports[`base configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -582,7 +583,8 @@ exports[`base configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ exports[`javascript configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -591,7 +592,8 @@ exports[`javascript configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process',
__USE_TEST_ID__: false,
__TEST__: false,
'process.env.NODE_ENV': '\\"development\\"'
}
Expand Down Expand Up @@ -616,6 +617,7 @@ exports[`react configuration > android > base config 1`] = `
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process',
__USE_TEST_ID__: false,
__TEST__: false,
'process.env.NODE_ENV': '\\"development\\"'
}
Expand Down Expand Up @@ -954,6 +956,7 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process',
__USE_TEST_ID__: false,
__TEST__: false,
'process.env.NODE_ENV': '\\"development\\"'
}
Expand Down Expand Up @@ -1286,6 +1289,7 @@ exports[`react configuration > ios > base config 1`] = `
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process',
__USE_TEST_ID__: false,
__TEST__: false,
'process.env.NODE_ENV': '\\"development\\"'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ exports[`svelte configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -630,7 +631,8 @@ exports[`svelte configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ exports[`typescript configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -591,7 +592,8 @@ exports[`typescript configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ exports[`vue configuration for android 1`] = `
__IOS__: false,
'global.isAndroid': true,
'global.isIOS': false,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down Expand Up @@ -656,7 +657,8 @@ exports[`vue configuration for ios 1`] = `
__IOS__: true,
'global.isAndroid': false,
'global.isIOS': true,
process: 'global.process'
process: 'global.process',
__USE_TEST_ID__: false
}
),
/* config.plugin('CopyWebpackPlugin') */
Expand Down
3 changes: 3 additions & 0 deletions packages/webpack5/src/configuration/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
/* for compat only */ 'global.isIOS': platform === 'ios',
process: 'global.process',

// enable testID when using --env.e2e
__USE_TEST_ID__: !!env.e2e,

// todo: ?!?!
// profile: '() => {}',
},
Expand Down
Loading