Skip to content
Open
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
6 changes: 6 additions & 0 deletions .changeset/great-zebras-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@react-native-macos/virtualized-lists": minor
"react-native-macos": minor
---

Remove the generic VirtualizedList and SectionList selection API, and keep macOS keyboard selection owned by FlatList. Option+Down selects the final data row, Enter activates the selected row, and native vertical inversion preserves child views.
Original file line number Diff line number Diff line change
Expand Up @@ -1747,7 +1747,8 @@ class ScrollView extends React.Component<ScrollViewProps, ScrollViewState> {
// this is the case.
const preserveChildren =
this.props.maintainVisibleContentPosition != null ||
(Platform.OS === 'android' && this.props.snapToAlignment != null);
(Platform.OS === 'android' && this.props.snapToAlignment != null) ||
(Platform.OS === 'macos' && this.props.inverted === true); // [macOS]

const contentContainer = (
<NativeScrollContentView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

const {create, unmount, update} = require('../../../../jest/renderer');
const Text = require('../../../Text/Text').default;
const Platform = require('../../../Utilities/Platform').default;
const ReactNativeTestTools = require('../../../Utilities/ReactNativeTestTools');
const View = require('../../View/View').default;
const ScrollView = require('../ScrollView').default;
Expand Down Expand Up @@ -39,6 +40,46 @@ describe('ScrollView', () => {
);
});

it('preserves children for native inversion on macOS', async () => {
const platformOSDescriptor = Object.getOwnPropertyDescriptor(
Platform,
'OS',
);
if (platformOSDescriptor == null) {
throw new Error('Platform.OS descriptor was not found');
}
// $FlowFixMe[incompatible-type] Exercise the macOS-only render branch.
Object.defineProperty(Platform, 'OS', {
...platformOSDescriptor,
// $FlowFixMe[incompatible-type] Platform is a platform-specific union.
value: 'macos',
});
try {
const invertedComponent = await create(
<ScrollView inverted={true}>
<View />
</ScrollView>,
);
const regularComponent = await create(
<ScrollView inverted={false}>
<View />
</ScrollView>,
);

expect(
invertedComponent.root.findByType('RCTScrollContentView').props
.collapsableChildren,
).toBe(false);
expect(
regularComponent.root.findByType('RCTScrollContentView').props
.collapsableChildren,
).toBe(true);
} finally {
// $FlowFixMe[incompatible-type] Restore the test platform.
Object.defineProperty(Platform, 'OS', platformOSDescriptor);
}
});

it('mocks native methods and instance methods', async () => {
jest.mock('../ScrollView');

Expand Down
70 changes: 67 additions & 3 deletions packages/react-native/Libraries/Lists/FlatList.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import type * as React from 'react';
import type {
ListRenderItem,
ListRenderItemInfo,
ViewToken,
VirtualizedListProps,
ViewabilityConfig,
Expand All @@ -19,7 +19,20 @@ import type {StyleProp} from '../StyleSheet/StyleSheet';
import type {ViewStyle} from '../StyleSheet/StyleSheetTypes';
import type {View} from '../Components/View/View';

export interface FlatListProps<ItemT> extends VirtualizedListProps<ItemT> {
export interface FlatListRenderItemInfo<ItemT>
extends ListRenderItemInfo<ItemT> {
isSelected: boolean;
}

export type FlatListRenderItem<ItemT> = (
info: FlatListRenderItemInfo<ItemT>,
) => React.ReactNode;

export interface FlatListProps<ItemT>
extends Omit<
VirtualizedListProps<ItemT>,
'ListItemComponent' | 'renderItem'
> {
/**
* Optional custom style for multi-item rows generated when numColumns > 1
*/
Expand Down Expand Up @@ -87,6 +100,44 @@ export interface FlatListProps<ItemT> extends VirtualizedListProps<ItemT> {
*/
initialScrollIndex?: number | null | undefined;

/**
* Allows rows to be selected with the keyboard.
*
* @platform macos
*/
enableSelectionOnKeyPress?: boolean | null | undefined;

/**
* The initially selected row.
*
* @platform macos
*/
initialSelectedIndex?: number | null | undefined;

/**
* Called when the selected row changes.
*
* @platform macos
*/
onSelectionChanged?:
| ((info: {
previousSelection: number;
newSelection: number;
item: ItemT | ReadonlyArray<ItemT> | null | undefined;
}) => void)
| null
| undefined;

/**
* Called when Enter is pressed on the selected row.
*
* @platform macos
*/
onSelectionEntered?:
| ((item: ItemT | ReadonlyArray<ItemT> | null | undefined) => void)
| null
| undefined;
Comment on lines +117 to +139

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these names taken from somewhere? Web API uses onChange for both. Looks like we've always had these so changing them now would break stuff.


/**
* Used to extract a unique key for a given item at the specified index. Key is used for caching
* and as the react key to track item re-ordering. The default extractor checks `item.key`, then
Expand Down Expand Up @@ -140,7 +191,13 @@ export interface FlatListProps<ItemT> extends VirtualizedListProps<ItemT> {
* ```
* Provides additional metadata like `index` if you need it.
*/
renderItem: ListRenderItem<ItemT> | null | undefined;
renderItem: FlatListRenderItem<ItemT> | null | undefined;

ListItemComponent?:
| React.ComponentType<FlatListRenderItemInfo<ItemT>>
| React.ReactElement
| null
| undefined;

/**
* See `ViewabilityHelper` for flow type and further documentation.
Expand Down Expand Up @@ -223,6 +280,13 @@ export abstract class FlatListComponent<
*/
flashScrollIndicators: () => void;

/**
* Moves selection to the specified row.
*
* @platform macos
*/
selectRowAtIndex: (index: number) => void;

/**
* Provides a handle to the underlying scroll responder.
*/
Expand Down
Loading
Loading