From f3b541553e9fb2c925048556b4180198619c8302 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:33:10 +0100 Subject: [PATCH 01/19] migrate to TS BaseOptionsList draft --- ...BaseOptionsList.js => BaseOptionsList.tsx} | 94 ++++-------- src/components/OptionsList/index.native.js | 19 --- src/components/OptionsList/index.native.tsx | 15 ++ .../OptionsList/{index.js => index.tsx} | 28 +--- .../OptionsList/optionsListPropTypes.js | 138 ------------------ src/components/OptionsList/types.ts | 127 ++++++++++++++++ src/libs/ReportUtils.ts | 2 +- 7 files changed, 180 insertions(+), 243 deletions(-) rename src/components/OptionsList/{BaseOptionsList.js => BaseOptionsList.tsx} (81%) delete mode 100644 src/components/OptionsList/index.native.js create mode 100644 src/components/OptionsList/index.native.tsx rename src/components/OptionsList/{index.js => index.tsx} (61%) delete mode 100644 src/components/OptionsList/optionsListPropTypes.js create mode 100644 src/components/OptionsList/types.ts diff --git a/src/components/OptionsList/BaseOptionsList.js b/src/components/OptionsList/BaseOptionsList.tsx similarity index 81% rename from src/components/OptionsList/BaseOptionsList.js rename to src/components/OptionsList/BaseOptionsList.tsx index bd3695eb7aa9..ce66abb0c03b 100644 --- a/src/components/OptionsList/BaseOptionsList.js +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,7 +1,5 @@ -import PropTypes from 'prop-types'; -import React, {forwardRef, memo, useEffect, useRef} from 'react'; -import {View} from 'react-native'; -import _ from 'underscore'; +import React, {forwardRef, memo, useEffect, useRef, ForwardedRef} from 'react'; +import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; @@ -10,31 +8,8 @@ import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import {defaultProps as optionsListDefaultProps, propTypes as optionsListPropTypes} from './optionsListPropTypes'; - -const propTypes = { - /** Determines whether the keyboard gets dismissed in response to a drag */ - keyboardDismissMode: PropTypes.string, - - /** Called when the user begins to drag the scroll view. Only used for the native component */ - onScrollBeginDrag: PropTypes.func, - - /** Callback executed on scroll. Only used for web/desktop component */ - onScroll: PropTypes.func, - - /** List styles for SectionList */ - listStyles: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]), - - ...optionsListPropTypes, -}; - -const defaultProps = { - keyboardDismissMode: 'none', - onScrollBeginDrag: () => {}, - onScroll: () => {}, - listStyles: [], - ...optionsListDefaultProps, -}; +import type {OptionData} from '@libs/ReportUtils'; +import type {BaseOptionListProps} from './types'; function BaseOptionsList({ keyboardDismissMode, @@ -66,24 +41,24 @@ function BaseOptionsList({ onSelectRow, boldStyle, isDisabled, - innerRef, isRowMultilineSupported, isLoadingNewOptions, nestedScrollEnabled, bounces, renderFooterContent, -}) { +}: BaseOptionListProps, ref: ForwardedRef) { const styles = useThemeStyles(); - const flattenedData = useRef(); + const flattenedData = useRef>([]); const previousSections = usePrevious(sections); const didLayout = useRef(false); - const listContainerStyles = listContainerStylesProp || [styles.flex1]; + const listContainerStyles = listContainerStylesProp ?? [styles.flex1]; /** * This helper function is used to memoize the computation needed for getItemLayout. It is run whenever section data changes. - * - * @returns {Array} */ const buildFlatSectionArray = () => { let offset = 0; @@ -92,6 +67,8 @@ function BaseOptionsList({ const flatArray = [{length: 0, offset}]; // Build the flat array + // TODO: Verify if we can use for of here + // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { const section = sections[sectionIndex]; // Add the section header @@ -119,7 +96,11 @@ function BaseOptionsList({ }; useEffect(() => { - if (_.isEqual(sections, previousSections)) { + // TODO: Verify if we can use isEqual here + // if (_.isEqual(sections, previousSections)) { + // return; + // } + if (sections === previousSections) { return; } flattenedData.current = buildFlatSectionArray(); @@ -150,8 +131,9 @@ function BaseOptionsList({ * * @returns {Object} */ - const getItemLayout = (data, flatDataArrayIndex) => { - if (!_.has(flattenedData.current, flatDataArrayIndex)) { + //SectionListProps, DefaultSectionT>.getItemLayout?: ((data: SectionListData, DefaultSectionT>[] | null, index: number) + const getItemLayout = (data: any, flatDataArrayIndex: number) => { + if (!flattenedData.current.has(flatDataArrayIndex)) { flattenedData.current = buildFlatSectionArray(); } @@ -165,10 +147,8 @@ function BaseOptionsList({ /** * Returns the key used by the list - * @param {Object} option - * @return {String} */ - const extractKey = (option) => option.keyForList; + const extractKey = (option: OptionData) => option.keyForList; /** * Function which renders a row in the list @@ -180,9 +160,10 @@ function BaseOptionsList({ * * @return {Component} */ - const renderItem = ({item, index, section}) => { + + const renderItem: SectionListRenderItem = ({item, index, section}) => { const isItemDisabled = isDisabled || section.isDisabled || !!item.isDisabled; - const isSelected = _.some(selectedOptions, (option) => { + const isSelected = selectedOptions?.some((option) => { if (option.accountID && option.accountID === item.accountID) { return true; } @@ -224,15 +205,10 @@ function BaseOptionsList({ /** * Function which renders a section header component - * - * @param {Object} params - * @param {Object} params.section - * @param {String} params.section.title - * @param {Boolean} params.section.shouldShow - * - * @return {Component} */ - const renderSectionHeader = ({section: {title, shouldShow}}) => { + const renderSectionHeader = ({section: {title, shouldShow}}: { + section: SectionListData + }) => { if (!title && shouldShow && !hideSectionHeaders && sectionHeaderStyle) { return ; } @@ -266,7 +242,7 @@ function BaseOptionsList({ ) : null} ( - - )), + forwardRef(BaseOptionsList), (prevProps, nextProps) => nextProps.focusedIndex === prevProps.focusedIndex && - nextProps.selectedOptions.length === prevProps.selectedOptions.length && + nextProps?.selectedOptions?.length === prevProps?.selectedOptions?.length && nextProps.headerMessage === prevProps.headerMessage && nextProps.isLoading === prevProps.isLoading && _.isEqual(nextProps.sections, prevProps.sections), diff --git a/src/components/OptionsList/index.native.js b/src/components/OptionsList/index.native.js deleted file mode 100644 index ab2db4f20967..000000000000 --- a/src/components/OptionsList/index.native.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, {forwardRef} from 'react'; -import {Keyboard} from 'react-native'; -import BaseOptionsList from './BaseOptionsList'; -import {defaultProps, propTypes} from './optionsListPropTypes'; - -const OptionsList = forwardRef((props, ref) => ( - Keyboard.dismiss()} - /> -)); - -OptionsList.propTypes = propTypes; -OptionsList.defaultProps = defaultProps; -OptionsList.displayName = 'OptionsList'; - -export default OptionsList; diff --git a/src/components/OptionsList/index.native.tsx b/src/components/OptionsList/index.native.tsx new file mode 100644 index 000000000000..ef81ea18976e --- /dev/null +++ b/src/components/OptionsList/index.native.tsx @@ -0,0 +1,15 @@ +import React, {ForwardedRef, forwardRef} from 'react'; +import {Keyboard, SectionList as RNSectionList} from 'react-native'; +import BaseOptionsList from './BaseOptionsList'; +import { OptionsListProps } from './types'; + +const OptionsList = forwardRef((props: OptionsListProps, ref: ForwardedRef) => ( + Keyboard.dismiss()} + /> +)); + +export default OptionsList; diff --git a/src/components/OptionsList/index.js b/src/components/OptionsList/index.tsx similarity index 61% rename from src/components/OptionsList/index.js rename to src/components/OptionsList/index.tsx index 36b8e7fccf12..45cf4f8aafce 100644 --- a/src/components/OptionsList/index.js +++ b/src/components/OptionsList/index.tsx @@ -1,12 +1,10 @@ -import React, {forwardRef, useCallback, useEffect, useRef} from 'react'; -import {Keyboard} from 'react-native'; -import _ from 'underscore'; -import withWindowDimensions from '@components/withWindowDimensions'; +import React, {ForwardedRef, forwardRef, useCallback, useEffect, useRef} from 'react'; +import {Keyboard, SectionList as RNSectionList} from 'react-native'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import BaseOptionsList from './BaseOptionsList'; -import {defaultProps, propTypes} from './optionsListPropTypes'; +import {OptionsListProps} from './types'; -function OptionsList(props) { +function OptionsList(props: OptionsListProps, ref: ForwardedRef) { const isScreenTouched = useRef(false); useEffect(() => { @@ -42,26 +40,12 @@ function OptionsList(props) { return ( ); } OptionsList.displayName = 'OptionsList'; -OptionsList.propTypes = propTypes; -OptionsList.defaultProps = defaultProps; -const OptionsListWithRef = forwardRef((props, ref) => ( - -)); - -OptionsListWithRef.displayName = 'OptionsListWithRef'; - -export default withWindowDimensions(OptionsListWithRef); +export default forwardRef(OptionsList); diff --git a/src/components/OptionsList/optionsListPropTypes.js b/src/components/OptionsList/optionsListPropTypes.js deleted file mode 100644 index 6008101ac1b6..000000000000 --- a/src/components/OptionsList/optionsListPropTypes.js +++ /dev/null @@ -1,138 +0,0 @@ -import PropTypes from 'prop-types'; -import optionPropTypes from '@components/optionPropTypes'; -import SectionList from '@components/SectionList'; -import stylePropTypes from '@styles/stylePropTypes'; - -const propTypes = { - /** option flexStyle for the options list container */ - listContainerStyles: PropTypes.arrayOf(PropTypes.object), - - /** Style for hovered state */ - // eslint-disable-next-line react/forbid-prop-types - optionHoveredStyle: PropTypes.object, - - /** Extra styles for the section list container */ - contentContainerStyles: PropTypes.arrayOf(PropTypes.object), - - /** Style for section headers */ - sectionHeaderStyle: stylePropTypes, - - /** Sections for the section list */ - sections: PropTypes.arrayOf( - PropTypes.shape({ - /** Title of the section */ - title: PropTypes.string, - - /** The initial index of this section given the total number of options in each section's data array */ - indexOffset: PropTypes.number, - - /** Array of options */ - data: PropTypes.arrayOf(optionPropTypes), - - /** Whether this section should show or not */ - shouldShow: PropTypes.bool, - }), - ), - - /** Index for option to focus on */ - focusedIndex: PropTypes.number, - - /** Array of already selected options */ - selectedOptions: PropTypes.arrayOf(optionPropTypes), - - /** Whether we can select multiple options or not */ - canSelectMultipleOptions: PropTypes.bool, - - /** Whether we highlight selected options */ - highlightSelectedOptions: PropTypes.bool, - - /** Whether to show headers above each section or not */ - hideSectionHeaders: PropTypes.bool, - - /** Whether to allow option focus or not */ - disableFocusOptions: PropTypes.bool, - - /** Display the text of the option in bold font style */ - boldStyle: PropTypes.bool, - - /** Callback to fire when a row is selected */ - onSelectRow: PropTypes.func, - - /** Optional header message */ - headerMessage: PropTypes.string, - - /** Passed via forwardRef so we can access the SectionList ref */ - innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(SectionList)})]), - - /** Whether to show the title tooltip */ - showTitleTooltip: PropTypes.bool, - - /** Whether to disable the interactivity of the list's option row(s) */ - isDisabled: PropTypes.bool, - - /** Whether the options list skeleton loading view should be displayed */ - isLoading: PropTypes.bool, - - /** Callback to execute when the SectionList lays out */ - onLayout: PropTypes.func, - - /** Whether to show a line separating options in list */ - shouldHaveOptionSeparator: PropTypes.bool, - - /** Whether to disable the inner padding in rows */ - shouldDisableRowInnerPadding: PropTypes.bool, - - /** Whether to prevent default focusing when selecting a row */ - shouldPreventDefaultFocusOnSelectRow: PropTypes.bool, - - /** Whether to show the scroll bar */ - showScrollIndicator: PropTypes.bool, - - /** Whether to wrap large text up to 2 lines */ - isRowMultilineSupported: PropTypes.bool, - - /** Whether we are loading new options */ - isLoadingNewOptions: PropTypes.bool, - - /** Whether nested scroll of options is enabled, true by default */ - nestedScrollEnabled: PropTypes.bool, - - /** Whether the list should have a bounce effect on iOS */ - bounces: PropTypes.bool, - - /** Custom content to display in the floating footer */ - renderFooterContent: PropTypes.func, -}; - -const defaultProps = { - optionHoveredStyle: undefined, - contentContainerStyles: [], - sectionHeaderStyle: undefined, - listContainerStyles: undefined, - sections: [], - focusedIndex: 0, - selectedOptions: [], - canSelectMultipleOptions: false, - highlightSelectedOptions: false, - hideSectionHeaders: false, - disableFocusOptions: false, - boldStyle: false, - onSelectRow: undefined, - headerMessage: '', - innerRef: null, - showTitleTooltip: false, - isDisabled: false, - isLoading: false, - onLayout: undefined, - shouldHaveOptionSeparator: false, - shouldDisableRowInnerPadding: false, - shouldPreventDefaultFocusOnSelectRow: false, - showScrollIndicator: false, - isRowMultilineSupported: false, - isLoadingNewOptions: false, - nestedScrollEnabled: true, - bounces: true, - renderFooterContent: undefined, -}; - -export {propTypes, defaultProps}; diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts new file mode 100644 index 000000000000..f8ee52c67647 --- /dev/null +++ b/src/components/OptionsList/types.ts @@ -0,0 +1,127 @@ +import { RefObject } from 'react'; +import type {OptionData} from '@libs/ReportUtils'; +import { StyleProp, ViewStyle, View, SectionList, SectionListData } from 'react-native'; + +// type SectionType = { +// /** Title of the section */ +// title?: string; + +// /** The initial index of this section given the total number of options in each section's data array */ +// indexOffset?: number; + +// /** Array of options */ +// data?: T[]; + +// /** Whether this section should show or not */ +// shouldShow?: boolean; +// } + +type OptionsListProps = { + /** option flexStyle for the options list container */ + listContainerStyles?: StyleProp; + + /** Style for hovered state */ + optionHoveredStyle?: StyleProp; + + /** Extra styles for the section list container */ + contentContainerStyles?: StyleProp; + + /** Style for section headers */ + sectionHeaderStyle?: StyleProp; + + /** Sections for the section list */ + sections: SectionListData; + + /** Index for option to focus on */ + focusedIndex?: number; + + /** Array of already selected options */ + selectedOptions?: OptionData[]; + + /** Whether we can select multiple options or not */ + canSelectMultipleOptions?: boolean; + + /** Whether we highlight selected options */ + highlightSelectedOptions?: boolean; + + /** Whether to show headers above each section or not */ + hideSectionHeaders?: boolean; + + /** Whether to allow option focus or not */ + disableFocusOptions?: boolean; + + /** Display the text of the option in bold font style */ + boldStyle?: boolean; + + /** Callback to fire when a row is selected */ + // TODO: Might want to use it from OptionRow + onSelectRow?: (option: OptionData, refElement: View | HTMLDivElement | null) => void | Promise; + + /** Optional header message */ + headerMessage?: string; + + /** Passed via forwardRef so we can access the SectionList ref */ + innerRef?: RefObject | ((instance: SectionList | null) => void); + + /** Whether to show the title tooltip */ + showTitleTooltip?: boolean; + + /** Whether to disable the interactivity of the list's option row(s) */ + isDisabled?: boolean; + + /** Whether the options list skeleton loading view should be displayed */ + isLoading?: boolean; + + /** Callback to execute when the SectionList lays out */ + onLayout?: () => void; + + /** Whether to show a line separating options in list */ + shouldHaveOptionSeparator?: boolean; + + /** Whether to disable the inner padding in rows */ + shouldDisableRowInnerPadding?: boolean; + + /** Whether to prevent default focusing when selecting a row */ + shouldPreventDefaultFocusOnSelectRow?: boolean; + + /** Whether to show the scroll bar */ + showScrollIndicator?: boolean; + + /** Whether to wrap large text up to 2 lines */ + isRowMultilineSupported?: boolean; + + /** Whether we are loading new options */ + isLoadingNewOptions?: boolean; + + /** Whether nested scroll of options is enabled, true by default */ + nestedScrollEnabled?: boolean; + + /** Whether the list should have a bounce effect on iOS */ + bounces?: boolean; + + /** Custom content to display in the floating footer */ + renderFooterContent?: () => JSX.Element; + + + // TODO: Verify below props + shouldShowMultipleOptionSelectorAsButton: boolean; + multipleOptionSelectorButtonText: string; + + onAddToSelection: () => void; +}; + +type BaseOptionListProps = OptionsListProps & { + /** Determines whether the keyboard gets dismissed in response to a drag */ + keyboardDismissMode?: 'none' | 'interactive' | 'on-drag'; + + /** Called when the user begins to drag the scroll view. Only used for the native component */ + onScrollBeginDrag: () => void; + + /** Callback executed on scroll. Only used for web/desktop component */ + onScroll: () => void; + + /** List styles for SectionList */ + listStyles: StyleProp; +} + +export type { OptionsListProps, SectionType, BaseOptionListProps}; \ No newline at end of file diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9404d832564a..59c5d15c139d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -336,7 +336,7 @@ type OptionData = { isUnread?: boolean | null; isUnreadWithMention?: boolean | null; hasDraftComment?: boolean | null; - keyForList?: string | null; + keyForList?: string; searchText?: string | null; isIOUReportOwner?: boolean | null; isArchivedRoom?: boolean | null; From 337584c6233f752a9ba56c9efc8fb7bce73ba659 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:30:04 +0100 Subject: [PATCH 02/19] fix some type errors --- .../OptionsList/BaseOptionsList.tsx | 31 ++++++++++-------- src/components/OptionsList/index.tsx | 2 ++ src/components/OptionsList/types.ts | 32 ++++++++++--------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index ce66abb0c03b..e8f1cd22bced 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,5 +1,5 @@ import React, {forwardRef, memo, useEffect, useRef, ForwardedRef} from 'react'; -import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData} from 'react-native'; +import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData, SectionList as SectionListType} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; @@ -52,7 +52,7 @@ function BaseOptionsList({ length: number; offset: number; }>>([]); - const previousSections = usePrevious(sections); + const previousSections = usePrevious>>>(sections); const didLayout = useRef(false); const listContainerStyles = listContainerStylesProp ?? [styles.flex1]; @@ -131,17 +131,19 @@ function BaseOptionsList({ * * @returns {Object} */ - //SectionListProps, DefaultSectionT>.getItemLayout?: ((data: SectionListData, DefaultSectionT>[] | null, index: number) - const getItemLayout = (data: any, flatDataArrayIndex: number) => { - if (!flattenedData.current.has(flatDataArrayIndex)) { + const getItemLayout = ( + data: Array>, + flatDataArrayIndex: number + ): {length: number, offset: number, index: number} => { + if (!flattenedData.current[flatDataArrayIndex]) { flattenedData.current = buildFlatSectionArray(); } - const targetItem = flattenedData.current[flatDataArrayIndex]; + return { length: targetItem.length, offset: targetItem.offset, - index: flatDataArrayIndex, + index: flatDataArrayIndex }; }; @@ -161,8 +163,8 @@ function BaseOptionsList({ * @return {Component} */ - const renderItem: SectionListRenderItem = ({item, index, section}) => { - const isItemDisabled = isDisabled || section.isDisabled || !!item.isDisabled; + const renderItem: SectionListRenderItem> = ({item, index, section}) => { + const isItemDisabled = isDisabled || section.isDisabled; // TODO: || !!item.isDisabled const isSelected = selectedOptions?.some((option) => { if (option.accountID && option.accountID === item.accountID) { return true; @@ -172,11 +174,12 @@ function BaseOptionsList({ return true; } - if (_.isEmpty(option.name)) { - return false; - } + // if (_.isEmpty(option.name)) { + // return false; + // } - return option.name === item.searchText; + // return option.name === item.searchText; + return false; }); return ( @@ -254,7 +257,7 @@ function BaseOptionsList({ contentContainerStyle={contentContainerStyles} showsVerticalScrollIndicator={showScrollIndicator} sections={sections} - keyExtractor={extractKey} + // keyExtractor={extractKey} stickySectixonHeadersEnabled={false} renderItem={renderItem} getItemLayout={getItemLayout} diff --git a/src/components/OptionsList/index.tsx b/src/components/OptionsList/index.tsx index 45cf4f8aafce..b99d897e32e3 100644 --- a/src/components/OptionsList/index.tsx +++ b/src/components/OptionsList/index.tsx @@ -40,6 +40,8 @@ function OptionsList(props: OptionsListProps, ref: ForwardedRef) return ( diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index f8ee52c67647..39ccb5148a1b 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -2,19 +2,19 @@ import { RefObject } from 'react'; import type {OptionData} from '@libs/ReportUtils'; import { StyleProp, ViewStyle, View, SectionList, SectionListData } from 'react-native'; -// type SectionType = { -// /** Title of the section */ -// title?: string; +type Section = { + /** Title of the section */ + title?: string; -// /** The initial index of this section given the total number of options in each section's data array */ -// indexOffset?: number; + /** The initial index of this section given the total number of options in each section's data array */ + indexOffset?: number; -// /** Array of options */ -// data?: T[]; + /** Array of options */ + data?: Item[]; -// /** Whether this section should show or not */ -// shouldShow?: boolean; -// } + /** Whether this section should show or not */ + shouldShow?: boolean; +} type OptionsListProps = { /** option flexStyle for the options list container */ @@ -30,7 +30,9 @@ type OptionsListProps = { sectionHeaderStyle?: StyleProp; /** Sections for the section list */ - sections: SectionListData; + // sections: Array>; + // sections: Array>; + sections: Array>>; /** Index for option to focus on */ focusedIndex?: number; @@ -115,13 +117,13 @@ type BaseOptionListProps = OptionsListProps & { keyboardDismissMode?: 'none' | 'interactive' | 'on-drag'; /** Called when the user begins to drag the scroll view. Only used for the native component */ - onScrollBeginDrag: () => void; + onScrollBeginDrag?: () => void; /** Callback executed on scroll. Only used for web/desktop component */ - onScroll: () => void; + onScroll?: () => void; /** List styles for SectionList */ - listStyles: StyleProp; + listStyles?: StyleProp; } -export type { OptionsListProps, SectionType, BaseOptionListProps}; \ No newline at end of file +export type { OptionsListProps, BaseOptionListProps, Section}; \ No newline at end of file From 47efedf8498797392a1ee2fd253a105a7a68bfb8 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:17:26 +0100 Subject: [PATCH 03/19] fix type in SectionList --- .../OptionsList/BaseOptionsList.tsx | 30 ++++++++++--------- src/components/OptionsList/types.ts | 10 +++++-- src/components/SectionList/types.ts | 5 +++- src/libs/ReportUtils.ts | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index e8f1cd22bced..5073d37fe19e 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,5 +1,5 @@ import React, {forwardRef, memo, useEffect, useRef, ForwardedRef} from 'react'; -import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData, SectionList as SectionListType} from 'react-native'; +import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; @@ -9,7 +9,8 @@ import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {OptionData} from '@libs/ReportUtils'; -import type {BaseOptionListProps} from './types'; +import _ from 'lodash'; +import type {BaseOptionListProps, Section} from './types'; function BaseOptionsList({ keyboardDismissMode, @@ -52,7 +53,7 @@ function BaseOptionsList({ length: number; offset: number; }>>([]); - const previousSections = usePrevious>>>(sections); + const previousSections = usePrevious>>(sections); const didLayout = useRef(false); const listContainerStyles = listContainerStylesProp ?? [styles.flex1]; @@ -119,8 +120,8 @@ function BaseOptionsList({ * This function is used to compute the layout of any given item in our list. * We need to implement it so that we can programmatically scroll to items outside the virtual render window of the SectionList. * - * @param {Array} data - This is the same as the data we pass into the component - * @param {Number} flatDataArrayIndex - This index is provided by React Native, and refers to a flat array with data from all the sections. This flat array has some quirks: + * @param data - This is the same as the data we pass into the component + * @param flatDataArrayIndex - This index is provided by React Native, and refers to a flat array with data from all the sections. This flat array has some quirks: * * 1. It ALWAYS includes a list header and a list footer, even if we don't provide/render those. * 2. Each section includes a header, even if we don't provide/render one. @@ -129,10 +130,10 @@ function BaseOptionsList({ * * [{header}, {sectionHeader}, {item}, {item}, {sectionHeader}, {item}, {item}, {footer}] * - * @returns {Object} + * @returns */ const getItemLayout = ( - data: Array>, + data: any, flatDataArrayIndex: number ): {length: number, offset: number, index: number} => { if (!flattenedData.current[flatDataArrayIndex]) { @@ -150,7 +151,7 @@ function BaseOptionsList({ /** * Returns the key used by the list */ - const extractKey = (option: OptionData) => option.keyForList; + const extractKey = (option: OptionData) => option.keyForList ?? ''; /** * Function which renders a row in the list @@ -163,8 +164,9 @@ function BaseOptionsList({ * @return {Component} */ - const renderItem: SectionListRenderItem> = ({item, index, section}) => { - const isItemDisabled = isDisabled || section.isDisabled; // TODO: || !!item.isDisabled + // const renderItem: SectionListRenderItem> = ({item, index, section}) => { + const renderItem: SectionListRenderItem = ({item, index, section}) => { + const isItemDisabled = isDisabled || section.isDisabled; // TODO: !!item.isDisabled const isSelected = selectedOptions?.some((option) => { if (option.accountID && option.accountID === item.accountID) { return true; @@ -184,7 +186,7 @@ function BaseOptionsList({ return ( + section: SectionListData }) => { if (!title && shouldShow && !hideSectionHeaders && sectionHeaderStyle) { return ; @@ -257,8 +259,8 @@ function BaseOptionsList({ contentContainerStyle={contentContainerStyles} showsVerticalScrollIndicator={showScrollIndicator} sections={sections} - // keyExtractor={extractKey} - stickySectixonHeadersEnabled={false} + keyExtractor={extractKey} + stickySectionHeadersEnabled={false} renderItem={renderItem} getItemLayout={getItemLayout} renderSectionHeader={renderSectionHeader} diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 39ccb5148a1b..72e13feec761 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -2,7 +2,7 @@ import { RefObject } from 'react'; import type {OptionData} from '@libs/ReportUtils'; import { StyleProp, ViewStyle, View, SectionList, SectionListData } from 'react-native'; -type Section = { +type Section = { /** Title of the section */ title?: string; @@ -10,10 +10,13 @@ type Section = { indexOffset?: number; /** Array of options */ - data?: Item[]; + data?: OptionData[]; /** Whether this section should show or not */ shouldShow?: boolean; + + /** Whether this section is disabled or not */ + isDisabled?: boolean; } type OptionsListProps = { @@ -32,7 +35,8 @@ type OptionsListProps = { /** Sections for the section list */ // sections: Array>; // sections: Array>; - sections: Array>>; + // sections: Array>>; + sections: Array>; /** Index for option to focus on */ focusedIndex?: number; diff --git a/src/components/SectionList/types.ts b/src/components/SectionList/types.ts index 093cb8f4e77c..64832583e063 100644 --- a/src/components/SectionList/types.ts +++ b/src/components/SectionList/types.ts @@ -1,8 +1,11 @@ +import { Section } from '@components/OptionsList/types'; +import { OptionData } from '@libs/ReportUtils'; import {ForwardedRef} from 'react'; import {SectionList, SectionListProps} from 'react-native'; +// TODO: Make it generic? type ForwardedSectionList = { - (props: SectionListProps, ref: ForwardedRef): React.ReactNode; + (props: SectionListProps, ref: ForwardedRef): React.ReactNode; displayName: string; }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 59c5d15c139d..9404d832564a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -336,7 +336,7 @@ type OptionData = { isUnread?: boolean | null; isUnreadWithMention?: boolean | null; hasDraftComment?: boolean | null; - keyForList?: string; + keyForList?: string | null; searchText?: string | null; isIOUReportOwner?: boolean | null; isArchivedRoom?: boolean | null; From 90f7a0c1a6dc43fa11b288a2ed4e374e8432086f Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 10:41:13 +0100 Subject: [PATCH 04/19] fix typing --- .../OptionsList/BaseOptionsList.tsx | 108 +++++++++--------- src/components/OptionsList/index.native.tsx | 6 +- src/components/OptionsList/index.tsx | 6 +- src/components/OptionsList/types.ts | 19 +-- src/components/SectionList/index.android.tsx | 30 ++--- src/components/SectionList/index.tsx | 22 ++-- src/components/SectionList/types.ts | 7 +- 7 files changed, 98 insertions(+), 100 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 5073d37fe19e..cba4b968c65c 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,58 +1,63 @@ -import React, {forwardRef, memo, useEffect, useRef, ForwardedRef} from 'react'; -import {View, SectionList as RNSectionList, SectionListRenderItem, SectionListData} from 'react-native'; +import _ from 'lodash'; +import React, {ForwardedRef, forwardRef, memo, useEffect, useRef} from 'react'; +import {SectionListData, SectionListRenderItem, View} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; import Text from '@components/Text'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {OptionData} from '@libs/ReportUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {OptionData} from '@libs/ReportUtils'; -import _ from 'lodash'; -import type {BaseOptionListProps, Section} from './types'; +import type {BaseOptionListProps, OptionsList, Section} from './types'; -function BaseOptionsList({ - keyboardDismissMode, - onScrollBeginDrag, - onScroll, - listStyles, - focusedIndex, - selectedOptions, - headerMessage, - isLoading, - sections, - onLayout, - hideSectionHeaders, - shouldHaveOptionSeparator, - showTitleTooltip, - optionHoveredStyle, - contentContainerStyles, - sectionHeaderStyle, - showScrollIndicator, - listContainerStyles: listContainerStylesProp, - shouldDisableRowInnerPadding, - shouldPreventDefaultFocusOnSelectRow, - disableFocusOptions, - canSelectMultipleOptions, - shouldShowMultipleOptionSelectorAsButton, - multipleOptionSelectorButtonText, - onAddToSelection, - highlightSelectedOptions, - onSelectRow, - boldStyle, - isDisabled, - isRowMultilineSupported, - isLoadingNewOptions, - nestedScrollEnabled, - bounces, - renderFooterContent, -}: BaseOptionListProps, ref: ForwardedRef) { +function BaseOptionsList( + { + keyboardDismissMode, + onScrollBeginDrag, + onScroll, + listStyles, + focusedIndex, + selectedOptions, + headerMessage, + isLoading, + sections, + onLayout, + hideSectionHeaders, + shouldHaveOptionSeparator, + showTitleTooltip, + optionHoveredStyle, + contentContainerStyles, + sectionHeaderStyle, + showScrollIndicator, + listContainerStyles: listContainerStylesProp, + shouldDisableRowInnerPadding, + shouldPreventDefaultFocusOnSelectRow, + disableFocusOptions, + canSelectMultipleOptions, + shouldShowMultipleOptionSelectorAsButton, + multipleOptionSelectorButtonText, + onAddToSelection, + highlightSelectedOptions, + onSelectRow, + boldStyle, + isDisabled, + isRowMultilineSupported, + isLoadingNewOptions, + nestedScrollEnabled, + bounces, + renderFooterContent, + }: BaseOptionListProps, + ref: ForwardedRef, +) { const styles = useThemeStyles(); - const flattenedData = useRef>([]); + const flattenedData = useRef< + Array<{ + length: number; + offset: number; + }> + >([]); const previousSections = usePrevious>>(sections); const didLayout = useRef(false); @@ -132,10 +137,7 @@ function BaseOptionsList({ * * @returns */ - const getItemLayout = ( - data: any, - flatDataArrayIndex: number - ): {length: number, offset: number, index: number} => { + const getItemLayout = (data: any, flatDataArrayIndex: number): {length: number; offset: number; index: number} => { if (!flattenedData.current[flatDataArrayIndex]) { flattenedData.current = buildFlatSectionArray(); } @@ -144,7 +146,7 @@ function BaseOptionsList({ return { length: targetItem.length, offset: targetItem.offset, - index: flatDataArrayIndex + index: flatDataArrayIndex, }; }; @@ -211,9 +213,7 @@ function BaseOptionsList({ /** * Function which renders a section header component */ - const renderSectionHeader = ({section: {title, shouldShow}}: { - section: SectionListData - }) => { + const renderSectionHeader = ({section: {title, shouldShow}}: {section: SectionListData}) => { if (!title && shouldShow && !hideSectionHeaders && sectionHeaderStyle) { return ; } @@ -246,7 +246,7 @@ function BaseOptionsList({ {headerMessage} ) : null} - ref={ref} style={listStyles} indicatorStyle="white" diff --git a/src/components/OptionsList/index.native.tsx b/src/components/OptionsList/index.native.tsx index ef81ea18976e..16258a6e5c43 100644 --- a/src/components/OptionsList/index.native.tsx +++ b/src/components/OptionsList/index.native.tsx @@ -1,9 +1,9 @@ import React, {ForwardedRef, forwardRef} from 'react'; -import {Keyboard, SectionList as RNSectionList} from 'react-native'; +import {Keyboard} from 'react-native'; import BaseOptionsList from './BaseOptionsList'; -import { OptionsListProps } from './types'; +import {OptionsListProps, OptionsList as OptionsListType} from './types'; -const OptionsList = forwardRef((props: OptionsListProps, ref: ForwardedRef) => ( +const OptionsList = forwardRef((props: OptionsListProps, ref: ForwardedRef) => ( ) { +function OptionsList(props: OptionsListProps, ref: ForwardedRef) { const isScreenTouched = useRef(false); useEffect(() => { diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 72e13feec761..a2075349ae19 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -1,23 +1,23 @@ -import { RefObject } from 'react'; +import {RefObject} from 'react'; +import {SectionList, SectionListData, StyleProp, View, ViewStyle} from 'react-native'; import type {OptionData} from '@libs/ReportUtils'; -import { StyleProp, ViewStyle, View, SectionList, SectionListData } from 'react-native'; type Section = { /** Title of the section */ - title?: string; + title: string; /** The initial index of this section given the total number of options in each section's data array */ - indexOffset?: number; + indexOffset: number; /** Array of options */ - data?: OptionData[]; + data: OptionData[]; /** Whether this section should show or not */ shouldShow?: boolean; /** Whether this section is disabled or not */ isDisabled?: boolean; -} +}; type OptionsListProps = { /** option flexStyle for the options list container */ @@ -108,7 +108,6 @@ type OptionsListProps = { /** Custom content to display in the floating footer */ renderFooterContent?: () => JSX.Element; - // TODO: Verify below props shouldShowMultipleOptionSelectorAsButton: boolean; multipleOptionSelectorButtonText: string; @@ -128,6 +127,8 @@ type BaseOptionListProps = OptionsListProps & { /** List styles for SectionList */ listStyles?: StyleProp; -} +}; + +type OptionsList = SectionList; -export type { OptionsListProps, BaseOptionListProps, Section}; \ No newline at end of file +export type {OptionsListProps, BaseOptionListProps, Section, OptionsList}; diff --git a/src/components/SectionList/index.android.tsx b/src/components/SectionList/index.android.tsx index 82477fffc3ee..c1e0b6d4b4f3 100644 --- a/src/components/SectionList/index.android.tsx +++ b/src/components/SectionList/index.android.tsx @@ -1,19 +1,19 @@ -import React, {forwardRef} from 'react'; -import {SectionList as RNSectionList} from 'react-native'; -import ForwardedSectionList from './types'; +import React, {ForwardedRef, forwardRef} from 'react'; +import {SectionList as RNSectionList, SectionListProps} from 'react-native'; -// eslint-disable-next-line react/function-component-definition -const SectionListWithRef: ForwardedSectionList = (props, ref) => ( - -); +function SectionListWithRef(props: SectionListProps, ref: ForwardedRef>) { + return ( + + ); +} SectionListWithRef.displayName = 'SectionListWithRef'; diff --git a/src/components/SectionList/index.tsx b/src/components/SectionList/index.tsx index 1c89b50468dd..5fb41bb3db89 100644 --- a/src/components/SectionList/index.tsx +++ b/src/components/SectionList/index.tsx @@ -1,15 +1,15 @@ -import React, {forwardRef} from 'react'; -import {SectionList as RNSectionList} from 'react-native'; -import ForwardedSectionList from './types'; +import React, {ForwardedRef, forwardRef} from 'react'; +import {SectionList as RNSectionList, SectionListProps} from 'react-native'; -// eslint-disable-next-line react/function-component-definition -const SectionList: ForwardedSectionList = (props, ref) => ( - -); +function SectionList(props: SectionListProps, ref: ForwardedRef>) { + return ( + + ); +} SectionList.displayName = 'SectionList'; diff --git a/src/components/SectionList/types.ts b/src/components/SectionList/types.ts index 64832583e063..d973b0b595cc 100644 --- a/src/components/SectionList/types.ts +++ b/src/components/SectionList/types.ts @@ -1,11 +1,8 @@ -import { Section } from '@components/OptionsList/types'; -import { OptionData } from '@libs/ReportUtils'; import {ForwardedRef} from 'react'; import {SectionList, SectionListProps} from 'react-native'; -// TODO: Make it generic? -type ForwardedSectionList = { - (props: SectionListProps, ref: ForwardedRef): React.ReactNode; +type ForwardedSectionList = { + (props: SectionListProps, ref: ForwardedRef): React.ReactNode; displayName: string; }; From eb76b1d56b6f0cd8aa6909689a3a21408d98fb4e Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 10:48:00 +0100 Subject: [PATCH 05/19] add default values --- .../OptionsList/BaseOptionsList.tsx | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index cba4b968c65c..f81df710f230 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -14,39 +14,39 @@ import type {BaseOptionListProps, OptionsList, Section} from './types'; function BaseOptionsList( { - keyboardDismissMode, - onScrollBeginDrag, - onScroll, - listStyles, - focusedIndex, - selectedOptions, - headerMessage, - isLoading, - sections, + keyboardDismissMode = 'none', + onScrollBeginDrag = () => {}, + onScroll = () => {}, + listStyles = [], + focusedIndex = 0, + selectedOptions = [], + headerMessage = '', + isLoading = false, + sections = [], onLayout, - hideSectionHeaders, - shouldHaveOptionSeparator, - showTitleTooltip, - optionHoveredStyle, - contentContainerStyles, - sectionHeaderStyle, - showScrollIndicator, + hideSectionHeaders = false, + shouldHaveOptionSeparator = false, + showTitleTooltip = false, + optionHoveredStyle = undefined, + contentContainerStyles = [], + sectionHeaderStyle = undefined, + showScrollIndicator = false, listContainerStyles: listContainerStylesProp, - shouldDisableRowInnerPadding, - shouldPreventDefaultFocusOnSelectRow, - disableFocusOptions, - canSelectMultipleOptions, + shouldDisableRowInnerPadding = false, + shouldPreventDefaultFocusOnSelectRow = false, + disableFocusOptions = false, + canSelectMultipleOptions = false, shouldShowMultipleOptionSelectorAsButton, multipleOptionSelectorButtonText, onAddToSelection, - highlightSelectedOptions, + highlightSelectedOptions = false, onSelectRow, - boldStyle, - isDisabled, - isRowMultilineSupported, - isLoadingNewOptions, - nestedScrollEnabled, - bounces, + boldStyle = false, + isDisabled = false, + isRowMultilineSupported = false, + isLoadingNewOptions = false, + nestedScrollEnabled = true, + bounces = true, renderFooterContent, }: BaseOptionListProps, ref: ForwardedRef, From 25f9ad4db92447461673ae5a1ce0f5a1c780f342 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:43:29 +0100 Subject: [PATCH 06/19] fix selected options --- .../OptionsList/BaseOptionsList.tsx | 37 +++++++++---------- src/components/OptionsList/types.ts | 14 ++++--- src/libs/ReportUtils.ts | 1 + 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index f81df710f230..b9d281916efc 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,6 +1,8 @@ -import _ from 'lodash'; -import React, {ForwardedRef, forwardRef, memo, useEffect, useRef} from 'react'; -import {SectionListData, SectionListRenderItem, View} from 'react-native'; +import {isEqual, isEmpty} from 'lodash'; +import type {ForwardedRef} from 'react'; +import React, { forwardRef, memo, useEffect, useRef} from 'react'; +import type {SectionListData, SectionListRenderItem} from 'react-native'; +import { View} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; @@ -73,10 +75,7 @@ function BaseOptionsList( const flatArray = [{length: 0, offset}]; // Build the flat array - // TODO: Verify if we can use for of here - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - const section = sections[sectionIndex]; + for (const section of sections) { // Add the section header const sectionHeaderHeight = section.title && !hideSectionHeaders ? variables.optionsListSectionHeaderHeight : 0; flatArray.push({length: sectionHeaderHeight, offset}); @@ -102,10 +101,9 @@ function BaseOptionsList( }; useEffect(() => { - // TODO: Verify if we can use isEqual here - // if (_.isEqual(sections, previousSections)) { - // return; - // } + if (isEqual(sections, previousSections)) { + return; + } if (sections === previousSections) { return; } @@ -137,7 +135,8 @@ function BaseOptionsList( * * @returns */ - const getItemLayout = (data: any, flatDataArrayIndex: number): {length: number; offset: number; index: number} => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const getItemLayout = (_data: Array> | null, flatDataArrayIndex: number) => { if (!flattenedData.current[flatDataArrayIndex]) { flattenedData.current = buildFlatSectionArray(); } @@ -166,9 +165,8 @@ function BaseOptionsList( * @return {Component} */ - // const renderItem: SectionListRenderItem> = ({item, index, section}) => { const renderItem: SectionListRenderItem = ({item, index, section}) => { - const isItemDisabled = isDisabled || section.isDisabled; // TODO: !!item.isDisabled + const isItemDisabled = isDisabled || !!section.isDisabled || !!item.isDisabled; const isSelected = selectedOptions?.some((option) => { if (option.accountID && option.accountID === item.accountID) { return true; @@ -178,12 +176,11 @@ function BaseOptionsList( return true; } - // if (_.isEmpty(option.name)) { - // return false; - // } + if (isEmpty(option.name)) { + return false; + } - // return option.name === item.searchText; - return false; + return option.name === item.searchText; }); return ( @@ -290,5 +287,5 @@ export default memo( nextProps?.selectedOptions?.length === prevProps?.selectedOptions?.length && nextProps.headerMessage === prevProps.headerMessage && nextProps.isLoading === prevProps.isLoading && - _.isEqual(nextProps.sections, prevProps.sections), + isEqual(nextProps.sections, prevProps.sections), ); diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index a2075349ae19..db74ae415330 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -1,5 +1,5 @@ -import {RefObject} from 'react'; -import {SectionList, SectionListData, StyleProp, View, ViewStyle} from 'react-native'; +import type {RefObject} from 'react'; +import type {SectionList, SectionListData, StyleProp, View, ViewStyle} from 'react-native'; import type {OptionData} from '@libs/ReportUtils'; type Section = { @@ -19,6 +19,11 @@ type Section = { isDisabled?: boolean; }; +type SelectedOptionData = OptionData & { + /** The name of selected option */ + name: string; +} + type OptionsListProps = { /** option flexStyle for the options list container */ listContainerStyles?: StyleProp; @@ -33,16 +38,13 @@ type OptionsListProps = { sectionHeaderStyle?: StyleProp; /** Sections for the section list */ - // sections: Array>; - // sections: Array>; - // sections: Array>>; sections: Array>; /** Index for option to focus on */ focusedIndex?: number; /** Array of already selected options */ - selectedOptions?: OptionData[]; + selectedOptions?: SelectedOptionData[]; /** Whether we can select multiple options or not */ canSelectMultipleOptions?: boolean; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 33a18b8534df..bfaea9ec508d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -351,6 +351,7 @@ type OptionData = { isTaskReport?: boolean | null; parentReportAction?: ReportAction; displayNamesWithTooltips?: DisplayNameWithTooltips | null; + isDisabled?: boolean | null; } & Report; type OnyxDataTaskAssigneeChat = { From f10208a35aa6cdc052f597556889163ccd8b8862 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:53:10 +0100 Subject: [PATCH 07/19] prettier & lint --- src/components/OptionsList/BaseOptionsList.tsx | 6 +++--- src/components/OptionsList/index.native.tsx | 5 +++-- src/components/OptionsList/index.tsx | 5 +++-- src/components/OptionsList/types.ts | 2 +- src/components/SectionList/index.android.tsx | 6 ++++-- src/components/SectionList/index.tsx | 6 ++++-- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index b9d281916efc..dc3bcb8a537e 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,8 +1,8 @@ -import {isEqual, isEmpty} from 'lodash'; +import {isEmpty, isEqual} from 'lodash'; import type {ForwardedRef} from 'react'; -import React, { forwardRef, memo, useEffect, useRef} from 'react'; +import React, {forwardRef, memo, useEffect, useRef} from 'react'; import type {SectionListData, SectionListRenderItem} from 'react-native'; -import { View} from 'react-native'; +import {View} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; import SectionList from '@components/SectionList'; diff --git a/src/components/OptionsList/index.native.tsx b/src/components/OptionsList/index.native.tsx index 16258a6e5c43..bc04228603ee 100644 --- a/src/components/OptionsList/index.native.tsx +++ b/src/components/OptionsList/index.native.tsx @@ -1,7 +1,8 @@ -import React, {ForwardedRef, forwardRef} from 'react'; +import React, {forwardRef} from 'react'; +import type {ForwardedRef} from 'react'; import {Keyboard} from 'react-native'; import BaseOptionsList from './BaseOptionsList'; -import {OptionsListProps, OptionsList as OptionsListType} from './types'; +import type {OptionsListProps, OptionsList as OptionsListType} from './types'; const OptionsList = forwardRef((props: OptionsListProps, ref: ForwardedRef) => ( ) { const isScreenTouched = useRef(false); diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index db74ae415330..feefafd348eb 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -22,7 +22,7 @@ type Section = { type SelectedOptionData = OptionData & { /** The name of selected option */ name: string; -} +}; type OptionsListProps = { /** option flexStyle for the options list container */ diff --git a/src/components/SectionList/index.android.tsx b/src/components/SectionList/index.android.tsx index 335dae322a5d..ab37bc733849 100644 --- a/src/components/SectionList/index.android.tsx +++ b/src/components/SectionList/index.android.tsx @@ -1,5 +1,7 @@ -import React, {type ForwardedRef, forwardRef} from 'react'; -import {SectionList as RNSectionList, type SectionListProps} from 'react-native'; +import React, {forwardRef} from 'react'; +import type {ForwardedRef} from 'react'; +import {SectionList as RNSectionList} from 'react-native'; +import type {SectionListProps} from 'react-native'; function SectionListWithRef(props: SectionListProps, ref: ForwardedRef>) { return ( diff --git a/src/components/SectionList/index.tsx b/src/components/SectionList/index.tsx index 32149640fd19..4671bbf34d78 100644 --- a/src/components/SectionList/index.tsx +++ b/src/components/SectionList/index.tsx @@ -1,5 +1,7 @@ -import React, {type ForwardedRef, forwardRef} from 'react'; -import {SectionList as RNSectionList, type SectionListProps} from 'react-native'; +import React, {forwardRef} from 'react'; +import type {ForwardedRef} from 'react'; +import {SectionList as RNSectionList} from 'react-native'; +import type {SectionListProps} from 'react-native'; function SectionList(props: SectionListProps, ref: ForwardedRef>) { return ( From 645dea1b2fc59bab99ef4b8de090120a3a8fab52 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:57:13 +0100 Subject: [PATCH 08/19] fix todos --- src/components/OptionsList/types.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index feefafd348eb..5219bd23be6c 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -62,8 +62,7 @@ type OptionsListProps = { boldStyle?: boolean; /** Callback to fire when a row is selected */ - // TODO: Might want to use it from OptionRow - onSelectRow?: (option: OptionData, refElement: View | HTMLDivElement | null) => void | Promise; + onSelectRow?: (option: OptionData, refElement: View) => void; /** Optional header message */ headerMessage?: string; @@ -110,10 +109,13 @@ type OptionsListProps = { /** Custom content to display in the floating footer */ renderFooterContent?: () => JSX.Element; - // TODO: Verify below props + /** Whether to show a button pill instead of a standard tickbox */ shouldShowMultipleOptionSelectorAsButton: boolean; + + /** Text for button pill */ multipleOptionSelectorButtonText: string; + /** Callback to fire when the multiple selector (tickbox or button) is clicked */ onAddToSelection: () => void; }; From 662d5795ad378efe4f8fcb6ad2b5a4b5f097cd63 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:07:44 +0100 Subject: [PATCH 09/19] fix types --- src/components/OptionsList/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 5219bd23be6c..946fb3adbeff 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -62,7 +62,7 @@ type OptionsListProps = { boldStyle?: boolean; /** Callback to fire when a row is selected */ - onSelectRow?: (option: OptionData, refElement: View) => void; + onSelectRow?: (option: OptionData, refElement: View | HTMLDivElement | null) => void | Promise; /** Optional header message */ headerMessage?: string; @@ -101,7 +101,7 @@ type OptionsListProps = { isLoadingNewOptions?: boolean; /** Whether nested scroll of options is enabled, true by default */ - nestedScrollEnabled?: boolean; + // nestedScrollEnabled?: boolean; /** Whether the list should have a bounce effect on iOS */ bounces?: boolean; From eb8015fe8e338533e579946eddd1f3627db46ed0 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:10:18 +0100 Subject: [PATCH 10/19] fix --- src/components/OptionsList/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 946fb3adbeff..8323038c50e1 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -101,7 +101,7 @@ type OptionsListProps = { isLoadingNewOptions?: boolean; /** Whether nested scroll of options is enabled, true by default */ - // nestedScrollEnabled?: boolean; + nestedScrollEnabled?: boolean; /** Whether the list should have a bounce effect on iOS */ bounces?: boolean; From 548aed27c33b3e7d868d76d1428d414e5044a22d Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:10:18 +0100 Subject: [PATCH 11/19] fix --- src/components/OptionsList/BaseOptionsList.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index dc3bcb8a537e..da80ca3e9eb4 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -104,9 +104,6 @@ function BaseOptionsList( if (isEqual(sections, previousSections)) { return; } - if (sections === previousSections) { - return; - } flattenedData.current = buildFlatSectionArray(); }); From 2b548e0a9201216a7308f3c29a504f833a2381fe Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 4 Jan 2024 13:54:18 +0100 Subject: [PATCH 12/19] type changes --- src/components/SectionList/index.android.tsx | 5 ++--- src/components/SectionList/index.tsx | 5 ++--- src/components/SectionList/types.ts | 10 ++++------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/SectionList/index.android.tsx b/src/components/SectionList/index.android.tsx index ab37bc733849..04cdd70e9ee2 100644 --- a/src/components/SectionList/index.android.tsx +++ b/src/components/SectionList/index.android.tsx @@ -1,9 +1,8 @@ import React, {forwardRef} from 'react'; -import type {ForwardedRef} from 'react'; import {SectionList as RNSectionList} from 'react-native'; -import type {SectionListProps} from 'react-native'; +import type {SectionListProps, SectionListRef} from './types'; -function SectionListWithRef(props: SectionListProps, ref: ForwardedRef>) { +function SectionListWithRef(props: SectionListProps, ref: SectionListRef) { return ( (props: SectionListProps, ref: ForwardedRef>) { +function SectionList(props: SectionListProps, ref: SectionListRef) { return ( = { - (props: SectionListProps, ref: ForwardedRef): React.ReactNode; - displayName: string; -}; +type SectionListProps = SectionListPropsRN; +type SectionListRef = ForwardedRef>; -export default ForwardedSectionList; +export type {SectionListProps, SectionListRef}; From 8832627063a279ca75f26addc0a502d5f519b55e Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Fri, 5 Jan 2024 08:38:58 +0100 Subject: [PATCH 13/19] address review --- .../OptionsList/BaseOptionsList.tsx | 8 ++++---- src/components/OptionsList/index.native.tsx | 20 ++++++++++--------- src/components/OptionsList/types.ts | 7 +------ src/libs/ReportUtils.ts | 1 + 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index da80ca3e9eb4..52015af5eaea 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -19,7 +19,7 @@ function BaseOptionsList( keyboardDismissMode = 'none', onScrollBeginDrag = () => {}, onScroll = () => {}, - listStyles = [], + listStyles, focusedIndex = 0, selectedOptions = [], headerMessage = '', @@ -29,9 +29,9 @@ function BaseOptionsList( hideSectionHeaders = false, shouldHaveOptionSeparator = false, showTitleTooltip = false, - optionHoveredStyle = undefined, - contentContainerStyles = [], - sectionHeaderStyle = undefined, + optionHoveredStyle, + contentContainerStyles, + sectionHeaderStyle, showScrollIndicator = false, listContainerStyles: listContainerStylesProp, shouldDisableRowInnerPadding = false, diff --git a/src/components/OptionsList/index.native.tsx b/src/components/OptionsList/index.native.tsx index bc04228603ee..9498368690e8 100644 --- a/src/components/OptionsList/index.native.tsx +++ b/src/components/OptionsList/index.native.tsx @@ -4,13 +4,15 @@ import {Keyboard} from 'react-native'; import BaseOptionsList from './BaseOptionsList'; import type {OptionsListProps, OptionsList as OptionsListType} from './types'; -const OptionsList = forwardRef((props: OptionsListProps, ref: ForwardedRef) => ( - Keyboard.dismiss()} - /> -)); +function OptionsList(props: OptionsListProps, ref: ForwardedRef) { + return ( + Keyboard.dismiss()} + /> + ); +} -export default OptionsList; +export default forwardRef(OptionsList); diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 8323038c50e1..860e8b51bcfb 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -19,11 +19,6 @@ type Section = { isDisabled?: boolean; }; -type SelectedOptionData = OptionData & { - /** The name of selected option */ - name: string; -}; - type OptionsListProps = { /** option flexStyle for the options list container */ listContainerStyles?: StyleProp; @@ -44,7 +39,7 @@ type OptionsListProps = { focusedIndex?: number; /** Array of already selected options */ - selectedOptions?: SelectedOptionData[]; + selectedOptions?: OptionData[]; /** Whether we can select multiple options or not */ canSelectMultipleOptions?: boolean; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8055cac4c649..22688a61c509 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -355,6 +355,7 @@ type OptionData = { parentReportAction?: ReportAction; displayNamesWithTooltips?: DisplayNameWithTooltips | null; isDisabled?: boolean | null; + name?: string | null; } & Report; type OnyxDataTaskAssigneeChat = { From 158257978fdeb1a4dcd58876a85d0f2ba8343566 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 8 Jan 2024 12:22:01 +0100 Subject: [PATCH 14/19] reuse SectionListData type --- src/components/OptionsList/BaseOptionsList.tsx | 10 +++++----- src/components/OptionsList/types.ts | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 52015af5eaea..855a311881ad 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,7 +1,7 @@ import {isEmpty, isEqual} from 'lodash'; import type {ForwardedRef} from 'react'; import React, {forwardRef, memo, useEffect, useRef} from 'react'; -import type {SectionListData, SectionListRenderItem} from 'react-native'; +import type {SectionListRenderItem} from 'react-native'; import {View} from 'react-native'; import OptionRow from '@components/OptionRow'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; @@ -12,7 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import type {OptionData} from '@libs/ReportUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {BaseOptionListProps, OptionsList, Section} from './types'; +import type {BaseOptionListProps, OptionsList, OptionsListData, Section} from './types'; function BaseOptionsList( { @@ -60,7 +60,7 @@ function BaseOptionsList( offset: number; }> >([]); - const previousSections = usePrevious>>(sections); + const previousSections = usePrevious(sections); const didLayout = useRef(false); const listContainerStyles = listContainerStylesProp ?? [styles.flex1]; @@ -133,7 +133,7 @@ function BaseOptionsList( * @returns */ // eslint-disable-next-line @typescript-eslint/naming-convention - const getItemLayout = (_data: Array> | null, flatDataArrayIndex: number) => { + const getItemLayout = (_data: OptionsListData[] | null, flatDataArrayIndex: number) => { if (!flattenedData.current[flatDataArrayIndex]) { flattenedData.current = buildFlatSectionArray(); } @@ -207,7 +207,7 @@ function BaseOptionsList( /** * Function which renders a section header component */ - const renderSectionHeader = ({section: {title, shouldShow}}: {section: SectionListData}) => { + const renderSectionHeader = ({section: {title, shouldShow}}: {section: OptionsListData}) => { if (!title && shouldShow && !hideSectionHeaders && sectionHeaderStyle) { return ; } diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 860e8b51bcfb..29e2409a7502 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -2,6 +2,9 @@ import type {RefObject} from 'react'; import type {SectionList, SectionListData, StyleProp, View, ViewStyle} from 'react-native'; import type {OptionData} from '@libs/ReportUtils'; +type OptionsList = SectionList; +type OptionsListData = SectionListData; + type Section = { /** Title of the section */ title: string; @@ -33,7 +36,7 @@ type OptionsListProps = { sectionHeaderStyle?: StyleProp; /** Sections for the section list */ - sections: Array>; + sections: OptionsListData[]; /** Index for option to focus on */ focusedIndex?: number; @@ -128,6 +131,4 @@ type BaseOptionListProps = OptionsListProps & { listStyles?: StyleProp; }; -type OptionsList = SectionList; - -export type {OptionsListProps, BaseOptionListProps, Section, OptionsList}; +export type {OptionsListProps, BaseOptionListProps, Section, OptionsList, OptionsListData}; From 9164721461d45e48ae2431276753ee45e82d34f8 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 8 Jan 2024 12:28:13 +0100 Subject: [PATCH 15/19] use isEmptyString instead of lodash --- src/components/OptionsList/BaseOptionsList.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 855a311881ad..c4902c957037 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,4 +1,4 @@ -import {isEmpty, isEqual} from 'lodash'; +import isEqual from 'lodash/isEqual'; import type {ForwardedRef} from 'react'; import React, {forwardRef, memo, useEffect, useRef} from 'react'; import type {SectionListRenderItem} from 'react-native'; @@ -10,6 +10,7 @@ import Text from '@components/Text'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import type {OptionData} from '@libs/ReportUtils'; +import StringUtils from '@libs/StringUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {BaseOptionListProps, OptionsList, OptionsListData, Section} from './types'; @@ -173,7 +174,7 @@ function BaseOptionsList( return true; } - if (isEmpty(option.name)) { + if (!option.name || StringUtils.isEmptyString(option.name)) { return false; } From 7257659ac0e5aa21bc178286231e8d6a29f00efb Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 10 Jan 2024 15:30:24 +0100 Subject: [PATCH 16/19] fix merge --- src/components/OptionsList/BaseOptionsList.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index c4902c957037..769b02e6e20b 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -15,6 +15,8 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {BaseOptionListProps, OptionsList, OptionsListData, Section} from './types'; +const viewabilityConfig = {viewAreaCoveragePercentThreshold: 95}; + function BaseOptionsList( { keyboardDismissMode = 'none', @@ -31,9 +33,10 @@ function BaseOptionsList( shouldHaveOptionSeparator = false, showTitleTooltip = false, optionHoveredStyle, - contentContainerStyles, + sectionHeaderStyle, showScrollIndicator = false, + contentContainerStyles: contentContainerStylesProp, listContainerStyles: listContainerStylesProp, shouldDisableRowInnerPadding = false, shouldPreventDefaultFocusOnSelectRow = false, @@ -51,6 +54,7 @@ function BaseOptionsList( nestedScrollEnabled = true, bounces = true, renderFooterContent, + safeAreaPaddingBottomStyle, }: BaseOptionListProps, ref: ForwardedRef, ) { @@ -64,7 +68,8 @@ function BaseOptionsList( const previousSections = usePrevious(sections); const didLayout = useRef(false); - const listContainerStyles = listContainerStylesProp ?? [styles.flex1]; + const listContainerStyles = useMemo(() => listContainerStylesProp || [styles.flex1], [listContainerStylesProp, styles.flex1]); + const contentContainerStyles = useMemo(() => [safeAreaPaddingBottomStyle, ...contentContainerStylesProp], [contentContainerStylesProp, safeAreaPaddingBottomStyle]); /** * This helper function is used to memoize the computation needed for getItemLayout. It is run whenever section data changes. @@ -263,7 +268,7 @@ function BaseOptionsList( initialNumToRender={12} maxToRenderPerBatch={CONST.MAX_TO_RENDER_PER_BATCH.DEFAULT} windowSize={5} - viewabilityConfig={{viewAreaCoveragePercentThreshold: 95}} + viewabilityConfig={viewabilityConfig} onViewableItemsChanged={onViewableItemsChanged} bounces={bounces} ListFooterComponent={renderFooterContent} From acc107029c32b54f529a7682e4c877d32f3eff94 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 10 Jan 2024 15:41:16 +0100 Subject: [PATCH 17/19] fixes --- src/components/OptionsList/BaseOptionsList.tsx | 7 +++---- src/components/OptionsList/index.native.tsx | 2 +- src/components/OptionsList/index.tsx | 5 ++--- src/components/OptionsList/types.ts | 3 +++ 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 769b02e6e20b..24de5300327a 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -1,6 +1,6 @@ import isEqual from 'lodash/isEqual'; import type {ForwardedRef} from 'react'; -import React, {forwardRef, memo, useEffect, useRef} from 'react'; +import React, {forwardRef, memo, useEffect, useMemo, useRef} from 'react'; import type {SectionListRenderItem} from 'react-native'; import {View} from 'react-native'; import OptionRow from '@components/OptionRow'; @@ -33,7 +33,6 @@ function BaseOptionsList( shouldHaveOptionSeparator = false, showTitleTooltip = false, optionHoveredStyle, - sectionHeaderStyle, showScrollIndicator = false, contentContainerStyles: contentContainerStylesProp, @@ -68,8 +67,8 @@ function BaseOptionsList( const previousSections = usePrevious(sections); const didLayout = useRef(false); - const listContainerStyles = useMemo(() => listContainerStylesProp || [styles.flex1], [listContainerStylesProp, styles.flex1]); - const contentContainerStyles = useMemo(() => [safeAreaPaddingBottomStyle, ...contentContainerStylesProp], [contentContainerStylesProp, safeAreaPaddingBottomStyle]); + const listContainerStyles = useMemo(() => listContainerStylesProp ?? [styles.flex1], [listContainerStylesProp, styles.flex1]); + const contentContainerStyles = useMemo(() => [safeAreaPaddingBottomStyle, contentContainerStylesProp], [contentContainerStylesProp, safeAreaPaddingBottomStyle]); /** * This helper function is used to memoize the computation needed for getItemLayout. It is run whenever section data changes. diff --git a/src/components/OptionsList/index.native.tsx b/src/components/OptionsList/index.native.tsx index 7cd807dffc7e..72c5db6538b1 100644 --- a/src/components/OptionsList/index.native.tsx +++ b/src/components/OptionsList/index.native.tsx @@ -17,4 +17,4 @@ function OptionsList(props: OptionsListProps, ref: ForwardedRef OptionsList.displayName = 'OptionsList'; -export default memo(forwardRef(OptionsList)); \ No newline at end of file +export default memo(forwardRef(OptionsList)); diff --git a/src/components/OptionsList/index.tsx b/src/components/OptionsList/index.tsx index 21047611c51d..0510a2a48ab2 100644 --- a/src/components/OptionsList/index.tsx +++ b/src/components/OptionsList/index.tsx @@ -1,6 +1,5 @@ -import React, {forwardRef, useCallback, useEffect, useRef} from 'react'; -import type {ForwardedRef} from 'react'; import React, {forwardRef, memo, useCallback, useEffect, useRef} from 'react'; +import type {ForwardedRef} from 'react'; import {Keyboard} from 'react-native'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import BaseOptionsList from './BaseOptionsList'; @@ -52,4 +51,4 @@ function OptionsList(props: OptionsListProps, ref: ForwardedRef OptionsList.displayName = 'OptionsList'; -export default forwardRef(OptionsList); +export default memo(forwardRef(OptionsList)); diff --git a/src/components/OptionsList/types.ts b/src/components/OptionsList/types.ts index 29e2409a7502..12e44adb5800 100644 --- a/src/components/OptionsList/types.ts +++ b/src/components/OptionsList/types.ts @@ -115,6 +115,9 @@ type OptionsListProps = { /** Callback to fire when the multiple selector (tickbox or button) is clicked */ onAddToSelection: () => void; + + /** Safe area style */ + safeAreaPaddingBottomStyle?: StyleProp; }; type BaseOptionListProps = OptionsListProps & { From e7385e90cd4975d9979ba116921819463b99fbd3 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:54:12 +0100 Subject: [PATCH 18/19] small cleanup --- src/components/OptionsList/BaseOptionsList.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index 8344d6437983..ba9a5884dd8b 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -130,8 +130,6 @@ function BaseOptionsList( * For example, given a list with two sections, two items in each section, no header, no footer, and no section headers, the flat array might look something like this: * * [{header}, {sectionHeader}, {item}, {item}, {sectionHeader}, {item}, {item}, {footer}] - * - * @returns */ // eslint-disable-next-line @typescript-eslint/naming-convention const getItemLayout = (_data: OptionsListData[] | null, flatDataArrayIndex: number) => { @@ -139,7 +137,6 @@ function BaseOptionsList( flattenedData.current = buildFlatSectionArray(); } const targetItem = flattenedData.current[flatDataArrayIndex]; - return { length: targetItem.length, offset: targetItem.offset, From 10071b02d99eb0b28b54e93cba6bbfdda71020f7 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:57:24 +0100 Subject: [PATCH 19/19] prettier --- src/components/OptionsList/BaseOptionsList.tsx | 2 +- src/components/OptionsList/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/OptionsList/BaseOptionsList.tsx b/src/components/OptionsList/BaseOptionsList.tsx index ba9a5884dd8b..c1e4562a0c2d 100644 --- a/src/components/OptionsList/BaseOptionsList.tsx +++ b/src/components/OptionsList/BaseOptionsList.tsx @@ -283,4 +283,4 @@ export default memo( nextProps.headerMessage === prevProps.headerMessage && nextProps.isLoading === prevProps.isLoading && isEqual(nextProps.sections, prevProps.sections), -); \ No newline at end of file +); diff --git a/src/components/OptionsList/index.tsx b/src/components/OptionsList/index.tsx index a054003acfa9..d0c6cb31bf64 100644 --- a/src/components/OptionsList/index.tsx +++ b/src/components/OptionsList/index.tsx @@ -51,4 +51,4 @@ function OptionsList(props: OptionsListProps, ref: ForwardedRef OptionsList.displayName = 'OptionsList'; -export default forwardRef(OptionsList); \ No newline at end of file +export default forwardRef(OptionsList);