Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TalkBack support for ScrollView accessibility announcements (list and grid) #33180

Closed
Closed
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
26d37b7
announce showing x of y items in flatlist
intergalacticspacehighway Jun 6, 2021
a40df5d
Merge branch 'master' into flatlist-a11y
intergalacticspacehighway Jun 19, 2021
ec09520
feat: grid accessibility announcement in flatlist
intergalacticspacehighway Jun 20, 2021
55877da
feat: nested flatlist example
intergalacticspacehighway Jun 20, 2021
49aec36
Remove .gitattributes causing error add_cacheinfo
fabOnReact Feb 24, 2022
2977e29
Merge branch 'main' into intergalactic-flatlist-update
fabOnReact Feb 24, 2022
60f7794
remove duplicate method getContentView
fabOnReact Feb 24, 2022
062cdcd
fix Runtime Error NoSuchKey exception columnCount
fabOnReact Feb 24, 2022
78b5fe3
rename accessibilityCollectionInfo to accessibilityCollection
fabOnReact Feb 24, 2022
80acf52
rename collectionItemInfoCompat to collectionItemCompat
fabOnReact Feb 24, 2022
cb2bc3b
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Feb 24, 2022
3b9a034
minor change
fabOnReact Feb 24, 2022
a89dd31
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Feb 25, 2022
1563620
minor change
fabOnReact Feb 25, 2022
cbd1029
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Feb 25, 2022
18f7772
yarn run lint --fix
fabOnReact Feb 26, 2022
2a76611
HorizontalSVManager has already property "accessibilityCollection".
fabOnReact Feb 26, 2022
e36b261
fix flow errors (more info in description)
fabOnReact Feb 26, 2022
32676eb
Fixing type flow errors
fabOnReact Feb 26, 2022
3a9e6d8
jest update FlatList snapshots
fabOnReact Feb 26, 2022
7ca9acd
remove accessibilityCollection from AndroidHorizontalScrollViewNative…
fabOnReact Feb 26, 2022
deb4d6b
format and adding some comments
fabOnReact Feb 26, 2022
42de4dc
fixing flow error for prop accessibilityCollectionItem
fabOnReact Feb 26, 2022
968db38
moving ScrollView accessibility to seperate class
fabOnReact Mar 7, 2022
3fcc666
ReactScrollViewADelegate (more info)
fabOnReact Mar 7, 2022
fc6a755
moving logic to method onInitialize..Internal
fabOnReact Mar 7, 2022
cd392c3
draft solution with refactoring
fabOnReact Mar 11, 2022
b7e5365
Avoid code duplication in AccessibilityDelegate logic
fabOnReact Mar 11, 2022
fc636f9
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 11, 2022
3fc34d4
Removing Generic and using check out Type
fabOnReact Mar 11, 2022
389761d
remove log
fabOnReact Mar 11, 2022
fc48b99
Delete file List.java
fabOnReact Mar 11, 2022
b97e5e1
change RNScrollDelegate methods to private
fabOnReact Mar 11, 2022
6062029
log soft exception if RSCADelegate is used in wrong class
fabOnReact Mar 11, 2022
6515fba
remove default value of false to isVisible
fabOnReact Mar 11, 2022
09be9f1
minor change
fabOnReact Mar 11, 2022
70e83a2
moving ReactScrollViewAccessibilityDelegate to /react/views/scroll
fabOnReact Mar 11, 2022
6bc0f5a
adding itemIndex variable as index is used twice
fabOnReact Mar 11, 2022
4c3182a
Removing container View in renderItem
fabOnReact Mar 13, 2022
fb795ad
draft
fabOnReact Mar 13, 2022
1a98f56
removing container View in renderItem callback
fabOnReact Mar 13, 2022
fe5dae5
rename accessibleItem to collectionItem
fabOnReact Mar 13, 2022
f5b3581
removing collectionItem variable
fabOnReact Mar 13, 2022
4e64a52
removing const {index} = info;
fabOnReact Mar 13, 2022
828fdd7
adding new prop to renderItem accessibilityCollectionItem
fabOnReact Mar 13, 2022
ce18d88
avoid adding new lines
fabOnReact Mar 13, 2022
f49caa4
Move Logic to virtualized list
fabOnReact Mar 13, 2022
7514735
move accessibilityCollectionItem logic to VirtList
fabOnReact Mar 14, 2022
fb30b84
Fix failure is caused by the missing prop type accessibilityCollectio…
fabOnReact Mar 14, 2022
4554dcc
fix VirtList flow errors
fabOnReact Mar 14, 2022
31c720f
fix error undefined accessibilityRole in SectionList
fabOnReact Mar 14, 2022
cd2fb42
fix flow errors (deleted )
fabOnReact Mar 14, 2022
f5ee947
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 14, 2022
3ead692
passing accessibilityCollectionItem to SectionList renderItem
fabOnReact Mar 14, 2022
a1dfa77
VList adding AccessibilityCollectionItem type
fabOnReact Mar 14, 2022
97c402b
adding grid role to AccessibilityRole type
fabOnReact Mar 14, 2022
6e59c55
update comments on accessibilityCollectionItem
fabOnReact Mar 14, 2022
623d9a9
yarn test Libraries/Lists/__tests__/VirtualizedList-test.js -u
fabOnReact Mar 14, 2022
0b64655
yarn test Libraries/Lists/__tests__/VirtualizedSectionList-test.js -u
fabOnReact Mar 14, 2022
f47df94
update FlatList-test.js snapshot
fabOnReact Mar 14, 2022
7269008
update SectionList snapshot tests
fabOnReact Mar 14, 2022
9ca3d2d
update accessibilityCollectionItem comments
fabOnReact Mar 14, 2022
2b9abe0
correct imports alphabetical order
fabOnReact Mar 15, 2022
489ebe4
use getChildAt instead of getContentView
fabOnReact Mar 15, 2022
3a11bff
add numColumns to restProps passed to VirtualizedList
fabOnReact Mar 15, 2022
d50fd1a
adding prop getCellsInItemCount to VirtualizedList
fabOnReact Mar 15, 2022
40085dc
adding link to comment
fabOnReact Mar 15, 2022
c99fcb8
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 15, 2022
1cdaf65
getCellsInItemCount is optional
fabOnReact Mar 15, 2022
c0893d2
add link to comment
fabOnReact Mar 15, 2022
cad423e
updating snapshot based on new behaviour introduced with https://gith…
fabOnReact Mar 16, 2022
7e12fb6
update snapshot based on changes done in https://github.com/fabriziob…
fabOnReact Mar 16, 2022
2cfaf4a
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 16, 2022
c3c01bd
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 22, 2022
6780e00
remove changes to getContentView
fabOnReact Mar 22, 2022
d647b46
adding /ReactAndroid/hermes-engine/.cxx to .gitignore
fabOnReact Mar 31, 2022
b754395
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Mar 31, 2022
53c455d
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Apr 4, 2022
d0d69a5
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Apr 6, 2022
0284797
Merge branch 'main' into intergalactic-flatlist-lastest-main
fabOnReact Apr 11, 2022
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
1 change: 1 addition & 0 deletions Libraries/Components/View/ViewAccessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type AccessibilityRole =
| 'tablist'
| 'timer'
| 'list'
| 'grid'
| 'toolbar';

// the info associated with an accessibility action
Expand Down
17 changes: 17 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,23 @@ export type ViewProps = $ReadOnly<{|
*/
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,

/**
*
* Node Information of a FlatList, VirtualizedList or SectionList collection item.
* A collection item starts at a given row and column in the collection, and spans one or more rows and columns.
*
* @platform android
*
*/
accessibilityCollectionItem?: ?{
rowIndex: number,
rowSpan: number,
columnIndex: number,
columnSpan: number,
heading: boolean,
itemIndex: number,
},

/**
* Specifies the nativeID of the associated label text. When the assistive technology focuses on the component with this props, the text is read aloud.
*
Expand Down
10 changes: 8 additions & 2 deletions Libraries/Lists/FlatList.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,17 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
return (
<View style={StyleSheet.compose(styles.row, columnWrapperStyle)}>
{item.map((it, kk) => {
const itemIndex = index * numColumns + kk;
const accessibilityCollectionItem = {
...info.accessibilityCollectionItem,
columnIndex: itemIndex % numColumns,
itemIndex: itemIndex,
};
const element = renderer({
item: it,
index: index * numColumns + kk,
index: itemIndex,
separators: info.separators,
accessibilityCollectionItem,
});
return element != null ? (
<React.Fragment key={kk}>{element}</React.Fragment>
Expand All @@ -634,7 +641,6 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {

render(): React.Node {
const {
numColumns,
columnWrapperStyle,
removeClippedSubviews: _removeClippedSubviews,
...restProps
Expand Down
70 changes: 67 additions & 3 deletions Libraries/Lists/VirtualizedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const Batchinator = require('../Interaction/Batchinator');
const FillRateHelper = require('./FillRateHelper');
const ReactNative = require('../Renderer/shims/ReactNative');
const RefreshControl = require('../Components/RefreshControl/RefreshControl');
const Platform = require('../Utilities/Platform');
const ScrollView = require('../Components/ScrollView/ScrollView');
const StyleSheet = require('../StyleSheet/StyleSheet');
const View = require('../Components/View/View');
Expand Down Expand Up @@ -51,10 +52,20 @@ export type Separators = {
...
};

export type AccessibilityCollectionItem = {
itemIndex: number,
rowIndex: number,
rowSpan: number,
columnIndex: number,
columnSpan: number,
heading: boolean,
};

export type RenderItemProps<ItemT> = {
item: ItemT,
index: number,
separators: Separators,
accessibilityCollectionItem: AccessibilityCollectionItem,
...
};

Expand Down Expand Up @@ -83,9 +94,19 @@ type RequiredProps = {|
*/
getItem: (data: any, index: number) => ?Item,
/**
* Determines how many items are in the data blob.
* Determines how many items (rows) are in the data blob.
*/
getItemCount: (data: any) => number,
/**
* Determines how many cells are in the data blob
* see https://bit.ly/35RKX7H
*/
getCellsInItemCount?: (data: any) => number,
/**
* The number of columns used in FlatList.
* The default of 1 is used in other components to calculate the accessibilityCollection prop.
*/
numColumns?: ?number,
|};
type OptionalProps = {|
renderItem?: ?RenderItemType<Item>,
Expand Down Expand Up @@ -305,6 +326,11 @@ type Props = {|
...OptionalProps,
|};

// numColumnsOrDefault(this.props.numColumns)
function numColumnsOrDefault(numColumns: ?number) {
return numColumns ?? 1;
}

let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';

Expand Down Expand Up @@ -1233,8 +1259,29 @@ class VirtualizedList extends React.PureComponent<Props, State> {
);
}

_getCellsInItemCount = props => {
const {getCellsInItemCount, data} = props;
if (getCellsInItemCount) return getCellsInItemCount(data);
if (Array.isArray(data)) return data.length;
return 0;
};

_defaultRenderScrollComponent = props => {
const {getItemCount, data} = props;
const onRefresh = props.onRefresh;
const numColumns = numColumnsOrDefault(props.numColumns);
const accessibilityRole = Platform.select({
android: numColumns > 1 ? 'grid' : 'list',
});
const rowCount = getItemCount(data);
const accessibilityCollection = {
// over-ride _getCellsInItemCount to handle Objects or other data formats
// see https://bit.ly/35RKX7H
itemCount: this._getCellsInItemCount(props),
rowCount,
columnCount: numColumns,
hierarchical: false,
};
if (this._isNestedWithSameOrientation()) {
// $FlowFixMe[prop-missing] - Typing ReactNativeComponent revealed errors
return <View {...props} />;
Expand All @@ -1249,6 +1296,8 @@ class VirtualizedList extends React.PureComponent<Props, State> {
// $FlowFixMe[prop-missing] Invalid prop usage
<ScrollView
{...props}
accessibilityRole={accessibilityRole}
accessibilityCollection={accessibilityCollection}
refreshControl={
props.refreshControl == null ? (
<RefreshControl
Expand All @@ -1263,8 +1312,14 @@ class VirtualizedList extends React.PureComponent<Props, State> {
/>
);
} else {
// $FlowFixMe[prop-missing] Invalid prop usage
return <ScrollView {...props} />;
return (
// $FlowFixMe[prop-missing] Invalid prop usage
<ScrollView
{...props}
accessibilityRole={accessibilityRole}
accessibilityCollection={accessibilityCollection}
/>
);
}
};

Expand Down Expand Up @@ -2003,10 +2058,19 @@ class CellRenderer extends React.Component<
}

if (renderItem) {
const accessibilityCollectionItem = {
itemIndex: index,
rowIndex: index,
rowSpan: 1,
columnIndex: 0,
columnSpan: 1,
heading: false,
};
return renderItem({
item,
index,
separators: this._separators,
accessibilityCollectionItem,
});
}

Expand Down
16 changes: 15 additions & 1 deletion Libraries/Lists/VirtualizedSectionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import invariant from 'invariant';
import type {ViewToken} from './ViewabilityHelper';
import type {AccessibilityCollectionItem} from './VirtualizedList';
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
import {View, VirtualizedList} from 'react-native';
import * as React from 'react';
Expand Down Expand Up @@ -338,7 +339,16 @@ class VirtualizedSectionList<

_renderItem =
(listItemCount: number) =>
({item, index}: {item: Item, index: number, ...}) => {
({
item,
index,
accessibilityCollectionItem,
}: {
item: Item,
index: number,
accessibilityCollectionItem: AccessibilityCollectionItem,
...
}) => {
const info = this._subExtractor(index);
if (!info) {
return null;
Expand Down Expand Up @@ -367,6 +377,7 @@ class VirtualizedSectionList<
LeadingSeparatorComponent={
infoIndex === 0 ? this.props.SectionSeparatorComponent : undefined
}
accessibilityCollectionItem={accessibilityCollectionItem}
cellKey={info.key}
index={infoIndex}
item={item}
Expand Down Expand Up @@ -479,6 +490,7 @@ type ItemWithSeparatorProps = $ReadOnly<{|
updatePropsFor: (prevCellKey: string, value: Object) => void,
renderItem: Function,
inverted: boolean,
accessibilityCollectionItem: AccessibilityCollectionItem,
|}>;

function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
Expand All @@ -496,6 +508,7 @@ function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
index,
section,
inverted,
accessibilityCollectionItem,
} = props;

const [leadingSeparatorHiglighted, setLeadingSeparatorHighlighted] =
Expand Down Expand Up @@ -569,6 +582,7 @@ function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node {
index,
section,
separators,
accessibilityCollectionItem,
});
const leadingSeparator = LeadingSeparatorComponent != null && (
<LeadingSeparatorComponent
Expand Down
59 changes: 59 additions & 0 deletions Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ exports[`FlatList renders all the bells and whistles 1`] = `
ListEmptyComponent={[Function]}
ListFooterComponent={[Function]}
ListHeaderComponent={[Function]}
accessibilityCollection={
Object {
"columnCount": 2,
"hierarchical": false,
"itemCount": 5,
"rowCount": 3,
}
}
data={
Array [
Object {
Expand All @@ -29,6 +37,7 @@ exports[`FlatList renders all the bells and whistles 1`] = `
getItemCount={[Function]}
getItemLayout={[Function]}
keyExtractor={[Function]}
numColumns={2}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
Expand Down Expand Up @@ -121,6 +130,14 @@ exports[`FlatList renders all the bells and whistles 1`] = `

exports[`FlatList renders empty list 1`] = `
<RCTScrollView
accessibilityCollection={
Object {
"columnCount": 1,
"hierarchical": false,
"itemCount": 0,
"rowCount": 0,
}
}
data={Array []}
getItem={[Function]}
getItemCount={[Function]}
Expand All @@ -144,6 +161,14 @@ exports[`FlatList renders empty list 1`] = `

exports[`FlatList renders null list 1`] = `
<RCTScrollView
accessibilityCollection={
Object {
"columnCount": 1,
"hierarchical": false,
"itemCount": 0,
"rowCount": 0,
}
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
Expand All @@ -166,6 +191,14 @@ exports[`FlatList renders null list 1`] = `

exports[`FlatList renders simple list (multiple columns) 1`] = `
<RCTScrollView
accessibilityCollection={
Object {
"columnCount": 2,
"hierarchical": false,
"itemCount": 3,
"rowCount": 2,
}
}
data={
Array [
Object {
Expand All @@ -182,6 +215,7 @@ exports[`FlatList renders simple list (multiple columns) 1`] = `
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
numColumns={2}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
Expand Down Expand Up @@ -237,6 +271,14 @@ exports[`FlatList renders simple list (multiple columns) 1`] = `

exports[`FlatList renders simple list 1`] = `
<RCTScrollView
accessibilityCollection={
Object {
"columnCount": 1,
"hierarchical": false,
"itemCount": 3,
"rowCount": 3,
}
}
data={
Array [
Object {
Expand Down Expand Up @@ -298,6 +340,14 @@ exports[`FlatList renders simple list 1`] = `
exports[`FlatList renders simple list using ListItemComponent (multiple columns) 1`] = `
<RCTScrollView
ListItemComponent={[Function]}
accessibilityCollection={
Object {
"columnCount": 2,
"hierarchical": false,
"itemCount": 3,
"rowCount": 2,
}
}
data={
Array [
Object {
Expand All @@ -314,6 +364,7 @@ exports[`FlatList renders simple list using ListItemComponent (multiple columns)
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
numColumns={2}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
Expand Down Expand Up @@ -369,6 +420,14 @@ exports[`FlatList renders simple list using ListItemComponent (multiple columns)
exports[`FlatList renders simple list using ListItemComponent 1`] = `
<RCTScrollView
ListItemComponent={[Function]}
accessibilityCollection={
Object {
"columnCount": 1,
"hierarchical": false,
"itemCount": 3,
"rowCount": 3,
}
}
data={
Array [
Object {
Expand Down
Loading