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

Workspace switcher page list refactor #40179

Merged
13 changes: 9 additions & 4 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native';
import isEmpty from 'lodash/isEmpty';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import type {LayoutChangeEvent, SectionList as RNSectionList, TextInput as RNTextInput, SectionListRenderItemInfo} from 'react-native';
import type {LayoutChangeEvent, SectionList as RNSectionList, TextInput as RNTextInput, SectionListData, SectionListRenderItemInfo} from 'react-native';
import {View} from 'react-native';
import Button from '@components/Button';
import Checkbox from '@components/Checkbox';
Expand Down Expand Up @@ -52,6 +52,7 @@ function BaseSelectionList<TItem extends ListItem>(
onConfirm,
headerContent,
footerContent,
listFooterContent,
showScrollIndicator = true,
showLoadingPlaceholder = false,
showConfirmButton = false,
Expand Down Expand Up @@ -294,7 +295,7 @@ function BaseSelectionList<TItem extends ListItem>(
*
* [{header}, {sectionHeader}, {item}, {item}, {sectionHeader}, {item}, {item}, {footer}]
*/
const getItemLayout = (data: Array<SectionListDataType<TItem>> | null, flatDataArrayIndex: number) => {
const getItemLayout = (data: Array<SectionListData<TItem, SectionWithIndexOffset<TItem>>> | null, flatDataArrayIndex: number) => {
const targetItem = flattenedSections.itemLayouts[flatDataArrayIndex];

if (!targetItem) {
Expand All @@ -313,6 +314,10 @@ function BaseSelectionList<TItem extends ListItem>(
};

const renderSectionHeader = ({section}: {section: SectionListDataType<TItem>}) => {
if (section.CustomSectionHeader) {
return <section.CustomSectionHeader section={section} />;
}

if (!section.title || isEmptyObject(section.data)) {
return null;
}
Expand All @@ -329,7 +334,7 @@ function BaseSelectionList<TItem extends ListItem>(
};

const renderItem = ({item, index, section}: SectionListRenderItemInfo<TItem, SectionWithIndexOffset<TItem>>) => {
const normalizedIndex = index + section.indexOffset;
const normalizedIndex = index + (section?.indexOffset ?? 0);
const isDisabled = !!section.isDisabled || item.isDisabled;
const isItemFocused = !isDisabled && (focusedIndex === normalizedIndex || itemsToHighlight?.has(item.keyForList ?? ''));
// We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
Expand Down Expand Up @@ -603,7 +608,7 @@ function BaseSelectionList<TItem extends ListItem>(
testID="selection-list"
onLayout={onSectionListLayout}
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
ListFooterComponent={ShowMoreButtonInstance}
ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
/>
{children}
</>
Expand Down
26 changes: 16 additions & 10 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import type RadioListItem from './RadioListItem';
import type TableListItem from './TableListItem';
import type UserListItem from './UserListItem';

type TRightHandSideComponent<TItem extends ListItem> = {
/** Component to display on the right side */
rightHandSideComponent?: ((item: TItem) => ReactElement | null | undefined) | ReactElement | null;
};

type CommonListItemProps<TItem extends ListItem> = {
/** Whether this item is focused (for arrow key controls) */
isFocused?: boolean;
Expand All @@ -34,9 +39,6 @@ type CommonListItemProps<TItem extends ListItem> = {
/** Callback to fire when an error is dismissed */
onDismissError?: (item: TItem) => void;

/** Component to display on the right side */
rightHandSideComponent?: ((item: TItem) => ReactElement<TItem> | null) | ReactElement | null;

/** Styles for the pressable component */
pressableStyle?: StyleProp<ViewStyle>;

Expand All @@ -54,7 +56,7 @@ type CommonListItemProps<TItem extends ListItem> = {

/** Handles what to do when the item is focused */
onFocus?: () => void;
};
} & TRightHandSideComponent<TItem>;

type ListItem = {
/** Text to display */
Expand Down Expand Up @@ -189,7 +191,7 @@ type SectionWithIndexOffset<TItem extends ListItem> = Section<TItem> & {

type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
/** Sections for the section list */
sections: Array<SectionListData<TItem, Section<TItem>>> | typeof CONST.EMPTY_ARRAY;
sections: Array<SectionListDataType<TItem>> | typeof CONST.EMPTY_ARRAY;

/** Default renderer for every item in the list */
ListItem: ValidListItem;
Expand Down Expand Up @@ -281,6 +283,9 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
/** Custom content to display in the footer */
footerContent?: ReactNode;

/** Custom content to display in the footer of list component. If present ShowMore button won't be displayed */
listFooterContent?: React.JSX.Element | null;

/** Whether to use dynamic maxToRenderPerBatch depending on the visible number of elements */
shouldUseDynamicMaxToRenderPerBatch?: boolean;

Expand All @@ -293,9 +298,6 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
/** Whether focus event should be delayed */
shouldDelayFocus?: boolean;

/** Component to display on the right side of each child */
rightHandSideComponent?: ((item: TItem) => ReactElement<TItem> | null) | ReactElement | null;

/** Whether to show the loading indicator for new options */
isLoadingNewOptions?: boolean;

Expand All @@ -322,7 +324,7 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
* When false, the list will render immediately and scroll to the bottom which works great for small lists.
*/
shouldHideListOnInitialRender?: boolean;
};
} & TRightHandSideComponent<TItem>;

type SelectionListHandle = {
scrollAndHighlightItem?: (items: string[], timeout: number) => void;
Expand All @@ -343,7 +345,11 @@ type FlattenedSectionsReturn<TItem extends ListItem> = {

type ButtonOrCheckBoxRoles = 'button' | 'checkbox';

type SectionListDataType<TItem extends ListItem> = SectionListData<TItem, SectionWithIndexOffset<TItem>>;
type ExtendedSectionListData<TItem extends ListItem, TSection extends SectionWithIndexOffset<TItem>> = SectionListData<TItem, TSection> & {
CustomSectionHeader?: ({section}: {section: TSection}) => ReactElement;
};

type SectionListDataType<TItem extends ListItem> = ExtendedSectionListData<TItem, SectionWithIndexOffset<TItem>>;

export type {
BaseSelectionListProps,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/getSectionsWithIndexOffset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {SectionListData} from 'react-native';
/**
* Returns a list of sections with indexOffset
*/
export default function getSectionsWithIndexOffset<ItemT, SectionT>(sections: Array<SectionListData<ItemT, SectionT>>): Array<SectionListData<ItemT, SectionT & {indexOffset: number}>> {
export default function getSectionsWithIndexOffset<ItemT, SectionT>(sections: Array<SectionListData<ItemT, SectionT>>): Array<SectionListData<ItemT, SectionT & {indexOffset?: number}>> {
return sections.map((section, index) => {
const indexOffset = [...sections].splice(0, index).reduce((acc, curr) => acc + (curr.data?.length ?? 0), 0);
return {...section, indexOffset};
Expand Down
Loading
Loading