mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
463af23753
Summary: This is the Javascript-only changes from D34518929 (https://github.com/facebook/react-native/commit/dd6325bafe1a539d348f3710e717a6344576b859), split out for push safety. Original summary and test plan below: This issue fixes [30977][17] . The Pull Request was previously published by [intergalacticspacehighway][13] with [31666][19]. The solution consists of: 1. Adding Javascript logic in the [FlatList][14], SectionList, VirtualizedList components to provide accessibility information (row and column position) for each cell in the method [renderItem][20] as a fourth parameter [accessibilityCollectionItem][21]. The information is saved on the native side in the AccessibilityNodeInfo and announced by TalkBack when changing row, column, or page ([video example][12]). The prop accessibilityCollectionItem is available in the View component which wraps each FlatList cell. 2. Adding Java logic in [ReactScrollView.java][16] and HorizontalScrollView to announce pages with TalkBack when scrolling up/down. The missing AOSP logic in [ScrollView.java][10] (see also the [GridView][11] example) is responsible for announcing Page Scrolling with TalkBack. Relevant Links: x [Additional notes on this PR][18] x [discussion on the additional container View around each FlatList cell][22] x [commit adding prop getCellsInItemCount to VirtualizedList][23] ## Changelog [Android] [Added] - Accessibility announcement for list and grid in FlatList Pull Request resolved: https://github.com/facebook/react-native/pull/33180 Test Plan: [1]. TalkBack announces pages and cells with Horizontal Flatlist in the Paper Renderer ([link][1]) [2]. TalkBack announces pages and cells with Vertical Flatlist in the Paper Renderer ([link][2]) [3]. `FlatList numColumns={undefined}` Should not trigger Runtime Error NoSuchKey exception columnCount when enabling TalkBack. ([link][3]) [4]. TalkBack announces pages and cells with Nested Horizontal Flatlist in the rn-tester app ([link][4]) [1]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050452894 [2]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050462465 [3]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1032340879 [4]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050618308 [10]:https://github.com/aosp-mirror/platform_frameworks_base/blob/1ac46f932ef88a8f96d652580d8105e361ffc842/core/java/android/widget/AdapterView.java#L1027-L1029 "GridView.java method responsible for calling setFromIndex and setToIndex" [11]:https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1042518901 "test case on Android GridView" [12]:https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050452894 "TalkBack announces pages and cells with Horizontal Flatlist in the Paper Renderer" [13]:https://github.com/intergalacticspacehighway "github intergalacticspacehighway" [14]:https://github.com/fabriziobertoglio1987/react-native/blob/80acf523a4410adac8005d5c9472fb87f78e12ee/Libraries/Lists/FlatList.js#L617-L636 "FlatList accessibilityCollectionItem" [16]:https://github.com/fabriziobertoglio1987/react-native/blob/5706bd7d3ee35dca48f85322a2bdcaec0bce2c85/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java#L183-L184 "logic added to ReactScrollView.java" [17]: https://github.com/facebook/react-native/issues/30977 [18]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6 [19]: https://github.com/facebook/react-native/pull/31666 [20]: https://reactnative.dev/docs/next/flatlist#required-renderitem "FlatList renderItem documentation" [21]: https://github.com/fabriziobertoglio1987/react-native/commit/75147359c5d070406ebbe488c57c3cd94c08c19d "commit that introduces fourth param accessibilityCollectionItem in callback renderItem" [22]: https://github.com/facebook/react-native/pull/33180#discussion_r826748664 "discussion on the additional container View around each FlatList cell" [23]: https://github.com/fabriziobertoglio1987/react-native/commit/d50fd1a68112f40f4be3ac3aa4d67f96df33e387 "commit adding prop getCellsInItemCount to VirtualizedList" Reviewed By: lunaleaps Differential Revision: D37668064 Pulled By: blavalla fbshipit-source-id: 7ba4068405fdcb9823d0daed2d8c36f0a56dbf0f
306 lines
11 KiB
JavaScript
306 lines
11 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
* @format
|
|
*/
|
|
|
|
import * as React from 'react';
|
|
import type {
|
|
ViewabilityConfig,
|
|
ViewabilityConfigCallbackPair,
|
|
ViewToken,
|
|
} from './ViewabilityHelper';
|
|
|
|
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
|
|
import typeof ScrollView from '../Components/ScrollView/ScrollView';
|
|
|
|
export type Item = any;
|
|
|
|
export type Separators = {
|
|
highlight: () => void,
|
|
unhighlight: () => void,
|
|
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
|
|
...
|
|
};
|
|
|
|
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,
|
|
...
|
|
};
|
|
|
|
export type RenderItemType<ItemT> = (
|
|
info: RenderItemProps<ItemT>,
|
|
) => React.Node;
|
|
|
|
type RequiredProps = {|
|
|
/**
|
|
* The default accessor functions assume this is an Array<{key: string} | {id: string}> but you can override
|
|
* getItem, getItemCount, and keyExtractor to handle any type of index-based data.
|
|
*/
|
|
data?: any,
|
|
/**
|
|
* A generic accessor for extracting an item from any sort of data blob.
|
|
*/
|
|
getItem: (data: any, index: number) => ?Item,
|
|
/**
|
|
* 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>,
|
|
/**
|
|
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
|
|
* implementation, but with a significant perf hit.
|
|
*/
|
|
debug?: ?boolean,
|
|
/**
|
|
* DEPRECATED: Virtualization provides significant performance and memory optimizations, but fully
|
|
* unmounts react instances that are outside of the render window. You should only need to disable
|
|
* this for debugging purposes. Defaults to false.
|
|
*/
|
|
disableVirtualization?: ?boolean,
|
|
/**
|
|
* A marker property for telling the list to re-render (since it implements `PureComponent`). If
|
|
* any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the
|
|
* `data` prop, stick it here and treat it immutably.
|
|
*/
|
|
extraData?: any,
|
|
// e.g. height, y
|
|
getItemLayout?: (
|
|
data: any,
|
|
index: number,
|
|
) => {
|
|
length: number,
|
|
offset: number,
|
|
index: number,
|
|
...
|
|
},
|
|
horizontal?: ?boolean,
|
|
/**
|
|
* How many items to render in the initial batch. This should be enough to fill the screen but not
|
|
* much more. Note these items will never be unmounted as part of the windowed rendering in order
|
|
* to improve perceived performance of scroll-to-top actions.
|
|
*/
|
|
initialNumToRender?: ?number,
|
|
/**
|
|
* Instead of starting at the top with the first item, start at `initialScrollIndex`. This
|
|
* disables the "scroll to top" optimization that keeps the first `initialNumToRender` items
|
|
* always rendered and immediately renders the items starting at this initial index. Requires
|
|
* `getItemLayout` to be implemented.
|
|
*/
|
|
initialScrollIndex?: ?number,
|
|
/**
|
|
* Reverses the direction of scroll. Uses scale transforms of -1.
|
|
*/
|
|
inverted?: ?boolean,
|
|
keyExtractor?: ?(item: Item, index: number) => string,
|
|
/**
|
|
* Each cell is rendered using this element. Can be a React Component Class,
|
|
* or a render function. Defaults to using View.
|
|
*/
|
|
CellRendererComponent?: ?React.ComponentType<any>,
|
|
/**
|
|
* Rendered in between each item, but not at the top or bottom. By default, `highlighted` and
|
|
* `leadingItem` props are provided. `renderItem` provides `separators.highlight`/`unhighlight`
|
|
* which will update the `highlighted` prop, but you can also add custom props with
|
|
* `separators.updateProps`.
|
|
*/
|
|
ItemSeparatorComponent?: ?React.ComponentType<any>,
|
|
/**
|
|
* Takes an item from `data` and renders it into the list. Example usage:
|
|
*
|
|
* <FlatList
|
|
* ItemSeparatorComponent={Platform.OS !== 'android' && ({highlighted}) => (
|
|
* <View style={[style.separator, highlighted && {marginLeft: 0}]} />
|
|
* )}
|
|
* data={[{title: 'Title Text', key: 'item1'}]}
|
|
* ListItemComponent={({item, separators}) => (
|
|
* <TouchableHighlight
|
|
* onPress={() => this._onPress(item)}
|
|
* onShowUnderlay={separators.highlight}
|
|
* onHideUnderlay={separators.unhighlight}>
|
|
* <View style={{backgroundColor: 'white'}}>
|
|
* <Text>{item.title}</Text>
|
|
* </View>
|
|
* </TouchableHighlight>
|
|
* )}
|
|
* />
|
|
*
|
|
* Provides additional metadata like `index` if you need it, as well as a more generic
|
|
* `separators.updateProps` function which let's you set whatever props you want to change the
|
|
* rendering of either the leading separator or trailing separator in case the more common
|
|
* `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for
|
|
* your use-case.
|
|
*/
|
|
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
/**
|
|
* Rendered when the list is empty. Can be a React Component Class, a render function, or
|
|
* a rendered element.
|
|
*/
|
|
ListEmptyComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
/**
|
|
* Rendered at the bottom of all the items. Can be a React Component Class, a render function, or
|
|
* a rendered element.
|
|
*/
|
|
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
/**
|
|
* Styling for internal View for ListFooterComponent
|
|
*/
|
|
ListFooterComponentStyle?: ViewStyleProp,
|
|
/**
|
|
* Rendered at the top of all the items. Can be a React Component Class, a render function, or
|
|
* a rendered element.
|
|
*/
|
|
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
/**
|
|
* Styling for internal View for ListHeaderComponent
|
|
*/
|
|
ListHeaderComponentStyle?: ViewStyleProp,
|
|
/**
|
|
* A unique identifier for this list. If there are multiple VirtualizedLists at the same level of
|
|
* nesting within another VirtualizedList, this key is necessary for virtualization to
|
|
* work properly.
|
|
*/
|
|
listKey?: string,
|
|
/**
|
|
* The maximum number of items to render in each incremental render batch. The more rendered at
|
|
* once, the better the fill rate, but responsiveness may suffer because rendering content may
|
|
* interfere with responding to button taps or other interactions.
|
|
*/
|
|
maxToRenderPerBatch?: ?number,
|
|
/**
|
|
* Called once when the scroll position gets within `onEndReachedThreshold` of the rendered
|
|
* content.
|
|
*/
|
|
onEndReached?: ?(info: {distanceFromEnd: number, ...}) => void,
|
|
/**
|
|
* How far from the end (in units of visible length of the list) the bottom edge of the
|
|
* list must be from the end of the content to trigger the `onEndReached` callback.
|
|
* Thus a value of 0.5 will trigger `onEndReached` when the end of the content is
|
|
* within half the visible length of the list. A value of 0 will not trigger until scrolling
|
|
* to the very end of the list.
|
|
*/
|
|
onEndReachedThreshold?: ?number,
|
|
/**
|
|
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
|
|
* sure to also set the `refreshing` prop correctly.
|
|
*/
|
|
onRefresh?: ?() => void,
|
|
/**
|
|
* Used to handle failures when scrolling to an index that has not been measured yet. Recommended
|
|
* action is to either compute your own offset and `scrollTo` it, or scroll as far as possible and
|
|
* then try again after more items have been rendered.
|
|
*/
|
|
onScrollToIndexFailed?: ?(info: {
|
|
index: number,
|
|
highestMeasuredFrameIndex: number,
|
|
averageItemLength: number,
|
|
...
|
|
}) => void,
|
|
/**
|
|
* Called when the viewability of rows changes, as defined by the
|
|
* `viewabilityConfig` prop.
|
|
*/
|
|
onViewableItemsChanged?: ?(info: {
|
|
viewableItems: Array<ViewToken>,
|
|
changed: Array<ViewToken>,
|
|
...
|
|
}) => void,
|
|
persistentScrollbar?: ?boolean,
|
|
/**
|
|
* Set this when offset is needed for the loading indicator to show correctly.
|
|
*/
|
|
progressViewOffset?: number,
|
|
/**
|
|
* A custom refresh control element. When set, it overrides the default
|
|
* <RefreshControl> component built internally. The onRefresh and refreshing
|
|
* props are also ignored. Only works for vertical VirtualizedList.
|
|
*/
|
|
refreshControl?: ?React.Element<any>,
|
|
/**
|
|
* Set this true while waiting for new data from a refresh.
|
|
*/
|
|
refreshing?: ?boolean,
|
|
/**
|
|
* Note: may have bugs (missing content) in some circumstances - use at your own risk.
|
|
*
|
|
* This may improve scroll performance for large lists.
|
|
*/
|
|
removeClippedSubviews?: boolean,
|
|
/**
|
|
* Render a custom scroll component, e.g. with a differently styled `RefreshControl`.
|
|
*/
|
|
renderScrollComponent?: (props: Object) => React.Element<any>,
|
|
/**
|
|
* Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off
|
|
* screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`.
|
|
*/
|
|
updateCellsBatchingPeriod?: ?number,
|
|
/**
|
|
* See `ViewabilityHelper` for flow type and further documentation.
|
|
*/
|
|
viewabilityConfig?: ViewabilityConfig,
|
|
/**
|
|
* List of ViewabilityConfig/onViewableItemsChanged pairs. A specific onViewableItemsChanged
|
|
* will be called when its corresponding ViewabilityConfig's conditions are met.
|
|
*/
|
|
viewabilityConfigCallbackPairs?: Array<ViewabilityConfigCallbackPair>,
|
|
/**
|
|
* Determines the maximum number of items rendered outside of the visible area, in units of
|
|
* visible lengths. So if your list fills the screen, then `windowSize={21}` (the default) will
|
|
* render the visible screen area plus up to 10 screens above and 10 below the viewport. Reducing
|
|
* this number will reduce memory consumption and may improve performance, but will increase the
|
|
* chance that fast scrolling may reveal momentary blank areas of unrendered content.
|
|
*/
|
|
windowSize?: ?number,
|
|
/**
|
|
* The legacy implementation is no longer supported.
|
|
*/
|
|
legacyImplementation?: empty,
|
|
|};
|
|
|
|
export type Props = {|
|
|
...React.ElementConfig<ScrollView>,
|
|
...RequiredProps,
|
|
...OptionalProps,
|
|
|};
|
|
|
|
/**
|
|
* Subset of properties needed to calculate frame metrics
|
|
*/
|
|
export type FrameMetricProps = {
|
|
data: RequiredProps['data'],
|
|
getItemCount: RequiredProps['getItemCount'],
|
|
getItem: RequiredProps['getItem'],
|
|
getItemLayout?: OptionalProps['getItemLayout'],
|
|
keyExtractor?: OptionalProps['keyExtractor'],
|
|
...
|
|
};
|