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

test lhn sidebar #35817

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions src/components/LHNOptionsList/LHNOptionsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {createContext} from 'react';
import type {RegisterOption} from './types';

type LHNOptionsContext = {
registerOption: RegisterOption;
};

export default createContext<LHNOptionsContext>({
registerOption: () => {
throw new Error('Registered option should be wrapped inside LHNOptionsList');
},
});
114 changes: 77 additions & 37 deletions src/components/LHNOptionsList/LHNOptionsList.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import {FlashList} from '@shopify/flash-list';
import type {ReactElement} from 'react';
import React, {useCallback} from 'react';
import {StyleSheet, View} from 'react-native';
import {FlatList, StyleSheet, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import withCurrentReportID from '@components/withCurrentReportID';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import SidebarUtils from '@libs/SidebarUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {OptionData} from '@src/libs/ReportUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import LHNOptionsContext from './LHNOptionsContext';
import OptionRowLHN from './OptionRowLHN';
import OptionRowLHNData from './OptionRowLHNData';
import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RenderItemProps} from './types';
import type {LHNOptionsListOnyxProps, LHNOptionsListProps, RegisterOption, RenderItemProps} from './types';

const keyExtractor = (item: string) => `report_${item}`;

Expand All @@ -36,6 +40,8 @@ function LHNOptionsList({
}: LHNOptionsListProps) {
const styles = useThemeStyles();
const {canUseViolations} = usePermissions();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [optionItems, setOptionItems] = React.useState<Record<string, OptionData | undefined>>({});

// When the first item renders we want to call the onFirstItemRendered callback.
// At this point in time we know that the list is actually displaying items.
Expand All @@ -49,10 +55,30 @@ function LHNOptionsList({
onFirstItemRendered();
}, [onFirstItemRendered]);

const registerOption = useCallback<RegisterOption>(
(optionItem: OptionData | undefined) => {
optionItems[optionItem?.reportID ?? ''] = optionItem;
// setOptionItems(items => ({
// ...items,
// [optionItem.reportID]: optionItem,
// }));
},
[optionItems],
);

const sortedReportIDs = SidebarUtils.getOrderedReportIDs(data, optionItems, optionMode);

const contextValue = React.useMemo(
() => ({
registerOption,
}),
[registerOption],
);

/**
* Function which renders a row in the list
* Function which prepares a row in the list
*/
const renderItem = useCallback(
const prepareItem = useCallback(
({item: reportID}: RenderItemProps): ReactElement => {
const itemFullReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] ?? null;
const itemReportActions = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? null;
Expand All @@ -75,50 +101,64 @@ function LHNOptionsList({
personalDetails={participantsPersonalDetails}
transaction={itemTransaction}
receiptTransactions={transactions}
viewMode={optionMode}
isFocused={!shouldDisableFocusOptions && reportID === currentReportID}
onSelectRow={onSelectRow}
preferredLocale={preferredLocale}
comment={itemComment}
transactionViolations={transactionViolations}
canUseViolations={canUseViolations}
/>
);
},
[draftComments, personalDetails, policy, preferredLocale, reportActions, reports, transactions, transactionViolations, canUseViolations],
);

/**
* Function which renders a row in the list
*/
const renderItem = useCallback(
({item: reportID}: RenderItemProps): ReactElement => {
const optionItem = optionItems[reportID];
return (
<OptionRowLHN
optionItem={optionItem}
isFocused={!shouldDisableFocusOptions && reportID === currentReportID}
reportID={reportID}
viewMode={optionMode}
onSelectRow={onSelectRow}
onLayout={onLayoutItem}
/>
);
},
[
currentReportID,
draftComments,
onSelectRow,
optionMode,
personalDetails,
policy,
preferredLocale,
reportActions,
reports,
shouldDisableFocusOptions,
transactions,
transactionViolations,
canUseViolations,
onLayoutItem,
],
[currentReportID, onLayoutItem, onSelectRow, optionItems, optionMode, shouldDisableFocusOptions],
);

return (
<View style={style ?? styles.flex1}>
<FlashList
indicatorStyle="white"
keyboardShouldPersistTaps="always"
contentContainerStyle={StyleSheet.flatten(contentContainerStyles)}
data={data}
testID="lhn-options-list"
keyExtractor={keyExtractor}
renderItem={renderItem}
estimatedItemSize={optionMode === CONST.OPTION_MODE.COMPACT ? variables.optionRowHeightCompact : variables.optionRowHeight}
extraData={[currentReportID]}
showsVerticalScrollIndicator={false}
/>
</View>
<>
<View style={styles.flex0}>
<LHNOptionsContext.Provider value={contextValue}>
<FlatList
data={data}
testID="lhn-options-list-virtualized"
keyExtractor={keyExtractor}
renderItem={prepareItem}
initialNumToRender={data.length}
/>
Comment on lines +133 to +139
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have this FlatList ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@shubham1206agra Friendly bump.

</LHNOptionsContext.Provider>
</View>
<View style={style ?? styles.flex1}>
<FlashList
indicatorStyle="white"
keyboardShouldPersistTaps="always"
contentContainerStyle={StyleSheet.flatten(contentContainerStyles)}
data={sortedReportIDs}
testID="lhn-options-list"
keyExtractor={keyExtractor}
renderItem={renderItem}
estimatedItemSize={optionMode === CONST.OPTION_MODE.COMPACT ? variables.optionRowHeightCompact : variables.optionRowHeight}
extraData={[currentReportID]}
showsVerticalScrollIndicator={false}
/>
</View>
</>
);
}

Expand Down
21 changes: 7 additions & 14 deletions src/components/LHNOptionsList/OptionRowLHNData.tsx
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's include @youssef-lr's suggestion #33029 (comment) in this component.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as TransactionUtils from '@libs/TransactionUtils';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import type {OptionData} from '@src/libs/ReportUtils';
import OptionRowLHN from './OptionRowLHN';
import LHNOptionsContext from './LHNOptionsContext';
import type {OptionRowLHNDataProps} from './types';

/*
Expand All @@ -17,7 +17,6 @@ import type {OptionRowLHNDataProps} from './types';
* re-render if the data really changed.
*/
function OptionRowLHNData({
isFocused = false,
fullReport,
reportActions,
personalDetails = {},
Expand All @@ -29,9 +28,9 @@ function OptionRowLHNData({
transaction,
transactionViolations,
canUseViolations,
...propsToForward
reportID,
}: OptionRowLHNDataProps) {
const reportID = propsToForward.reportID;
const {registerOption} = React.useContext(LHNOptionsContext);

const optionItemRef = useRef<OptionData>();
const linkedTransaction = useMemo(() => {
Expand Down Expand Up @@ -59,29 +58,23 @@ function OptionRowLHNData({
}

optionItemRef.current = item;
registerOption(item);

return item;
// Listen parentReportAction to update title of thread report when parentReportAction changed
// Listen to transaction to update title of transaction report when transaction changed
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction, transactionViolations, canUseViolations]);
}, [fullReport, reportActions, personalDetails, preferredLocale, policy, parentReportAction, hasViolations, linkedTransaction, transaction]);

useEffect(() => {
if (!optionItem || !!optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) {
if (!optionItem || !!optionItem.hasDraftComment || !comment || comment.length <= 0) {
return;
}
Report.setReportWithDraft(reportID, true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<OptionRowLHN
// eslint-disable-next-line react/jsx-props-no-spreading
{...propsToForward}
isFocused={isFocused}
optionItem={optionItem}
/>
);
return null;
}

OptionRowLHNData.displayName = 'OptionRowLHNData';
Expand Down
16 changes: 3 additions & 13 deletions src/components/LHNOptionsList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type {Locale, PersonalDetailsList, Policy, Report, ReportAction, ReportAc

type OptionMode = ValueOf<typeof CONST.OPTION_MODE>;

type RegisterOption = (option: OptionData | undefined) => void;

type LHNOptionsListOnyxProps = {
/** The policy which the user has access to and which the report could be tied to */
policy: OnyxCollection<Policy>;
Expand Down Expand Up @@ -62,9 +64,6 @@ type CustomLHNOptionsListProps = {
type LHNOptionsListProps = CustomLHNOptionsListProps & CurrentReportIDContextValue & LHNOptionsListOnyxProps;

type OptionRowLHNDataProps = {
/** Whether row should be focused */
isFocused?: boolean;

/** List of users' personal details */
personalDetails?: PersonalDetailsList;

Expand Down Expand Up @@ -100,15 +99,6 @@ type OptionRowLHNDataProps = {

/** Whether the user can use violations */
canUseViolations: boolean | undefined;

/** Toggle between compact and default view */
viewMode?: OptionMode;

/** A function that is called when an option is selected. Selected option is passed as a param */
onSelectRow?: (optionItem: OptionData, popoverAnchor: RefObject<View>) => void;

/** Callback to execute when the OptionList lays out */
onLayout?: (event: LayoutChangeEvent) => void;
};

type OptionRowLHNProps = {
Expand All @@ -135,4 +125,4 @@ type OptionRowLHNProps = {

type RenderItemProps = {item: string};

export type {LHNOptionsListProps, OptionRowLHNDataProps, OptionRowLHNProps, LHNOptionsListOnyxProps, RenderItemProps};
export type {LHNOptionsListProps, OptionRowLHNDataProps, OptionRowLHNProps, LHNOptionsListOnyxProps, RenderItemProps, RegisterOption};
Loading
Loading