Skip to content

Commit cb5c597

Browse files
authored
Merge pull request #38719 from Krishna2323/krishna2323/issue/37782
fix: Fix scrollable elements in Policy pages.
2 parents 6fd7fbb + d343397 commit cb5c597

File tree

8 files changed

+111
-72
lines changed

8 files changed

+111
-72
lines changed

src/components/SelectionList/BaseSelectionList.tsx

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ function BaseSelectionList<TItem extends ListItem>(
7777
sectionTitleStyles,
7878
textInputAutoFocus = true,
7979
shouldTextInputInterceptSwipe = false,
80+
listHeaderContent,
8081
onEndReached = () => {},
8182
onEndReachedThreshold,
8283
}: BaseSelectionListProps<TItem>,
@@ -334,7 +335,7 @@ function BaseSelectionList<TItem extends ListItem>(
334335
return <section.CustomSectionHeader section={section} />;
335336
}
336337

337-
if (!section.title || isEmptyObject(section.data)) {
338+
if (!section.title || isEmptyObject(section.data) || listHeaderContent) {
338339
return null;
339340
}
340341

@@ -349,6 +350,39 @@ function BaseSelectionList<TItem extends ListItem>(
349350
);
350351
};
351352

353+
const header = () => (
354+
<>
355+
{!headerMessage && canSelectMultiple && shouldShowSelectAll && (
356+
<View style={[styles.userSelectNone, styles.peopleRow, styles.ph5, styles.pb3, listHeaderWrapperStyle, styles.selectionListStickyHeader]}>
357+
<View style={[styles.flexRow, styles.alignItemsCenter]}>
358+
<Checkbox
359+
accessibilityLabel={translate('workspace.people.selectAll')}
360+
isChecked={flattenedSections.allSelected}
361+
onPress={selectAllRow}
362+
disabled={flattenedSections.allOptions.length === flattenedSections.disabledOptionsIndexes.length}
363+
/>
364+
{!customListHeader && (
365+
<PressableWithFeedback
366+
style={[styles.userSelectNone, styles.flexRow, styles.alignItemsCenter]}
367+
onPress={selectAllRow}
368+
accessibilityLabel={translate('workspace.people.selectAll')}
369+
role="button"
370+
accessibilityState={{checked: flattenedSections.allSelected}}
371+
disabled={flattenedSections.allOptions.length === flattenedSections.disabledOptionsIndexes.length}
372+
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
373+
onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined}
374+
>
375+
<Text style={[styles.textStrong, styles.ph3]}>{translate('workspace.people.selectAll')}</Text>
376+
</PressableWithFeedback>
377+
)}
378+
</View>
379+
{customListHeader}
380+
</View>
381+
)}
382+
{!headerMessage && !canSelectMultiple && customListHeader}
383+
</>
384+
);
385+
352386
const renderItem = ({item, index, section}: SectionListRenderItemInfo<TItem, SectionWithIndexOffset<TItem>>) => {
353387
const normalizedIndex = index + (section?.indexOffset ?? 0);
354388
const isDisabled = !!section.isDisabled || item.isDisabled;
@@ -580,39 +614,17 @@ function BaseSelectionList<TItem extends ListItem>(
580614
<OptionsListSkeletonView shouldAnimate />
581615
) : (
582616
<>
583-
{!headerMessage && canSelectMultiple && shouldShowSelectAll && (
584-
<View style={[styles.userSelectNone, styles.peopleRow, styles.ph5, styles.pb3, listHeaderWrapperStyle]}>
585-
<View style={[styles.flexRow, styles.alignItemsCenter]}>
586-
<Checkbox
587-
accessibilityLabel={translate('workspace.people.selectAll')}
588-
isChecked={flattenedSections.allSelected}
589-
onPress={selectAllRow}
590-
disabled={flattenedSections.allOptions.length === flattenedSections.disabledOptionsIndexes.length}
591-
/>
592-
{!customListHeader && (
593-
<PressableWithFeedback
594-
style={[styles.userSelectNone, styles.flexRow, styles.alignItemsCenter]}
595-
onPress={selectAllRow}
596-
accessibilityLabel={translate('workspace.people.selectAll')}
597-
role="button"
598-
accessibilityState={{checked: flattenedSections.allSelected}}
599-
disabled={flattenedSections.allOptions.length === flattenedSections.disabledOptionsIndexes.length}
600-
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
601-
onMouseDown={shouldPreventDefaultFocusOnSelectRow ? (e) => e.preventDefault() : undefined}
602-
>
603-
<Text style={[styles.textStrong, styles.ph3]}>{translate('workspace.people.selectAll')}</Text>
604-
</PressableWithFeedback>
605-
)}
606-
</View>
607-
{customListHeader}
608-
</View>
609-
)}
610-
{!headerMessage && !canSelectMultiple && customListHeader}
617+
{!listHeaderContent && header()}
611618
<SectionList
612619
ref={listRef}
613620
sections={slicedSections}
614621
stickySectionHeadersEnabled={false}
615-
renderSectionHeader={renderSectionHeader}
622+
renderSectionHeader={(arg) => (
623+
<>
624+
{renderSectionHeader(arg)}
625+
{listHeaderContent && header()}
626+
</>
627+
)}
616628
renderItem={renderItem}
617629
getItemLayout={getItemLayout}
618630
onScroll={onScroll}
@@ -631,6 +643,7 @@ function BaseSelectionList<TItem extends ListItem>(
631643
onLayout={onSectionListLayout}
632644
style={(!maxToRenderPerBatch || (shouldHideListOnInitialRender && isInitialSectionListRender)) && styles.opacity0}
633645
ListFooterComponent={listFooterContent ?? ShowMoreButtonInstance}
646+
ListHeaderComponent={listHeaderContent && listHeaderContent}
634647
onEndReached={onEndReached}
635648
onEndReachedThreshold={onEndReachedThreshold}
636649
/>

src/components/SelectionList/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
321321
/** Custom content to display in the header */
322322
headerContent?: ReactNode;
323323

324+
/** Custom content to display in the header of list component. */
325+
listHeaderContent?: React.JSX.Element | null;
326+
324327
/** Custom content to display in the footer */
325328
footerContent?: ReactNode;
326329

src/pages/workspace/WorkspaceMembersPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
504504
onPress={() => null}
505505
options={getBulkActionsButtonOptions()}
506506
isSplitButton={false}
507-
style={[isSmallScreenWidth && styles.flexGrow1]}
507+
style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]}
508508
/>
509509
) : (
510510
<Button
@@ -514,7 +514,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
514514
text={translate('workspace.invite.member')}
515515
icon={Expensicons.Plus}
516516
innerStyles={[isSmallScreenWidth && styles.alignItemsCenter]}
517-
style={[isSmallScreenWidth && styles.flexGrow1]}
517+
style={[isSmallScreenWidth && styles.flexGrow1, isSmallScreenWidth && styles.mb3]}
518518
/>
519519
)}
520520
</View>
@@ -572,7 +572,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
572572
ListItem={TableListItem}
573573
disableKeyboardShortcuts={removeMembersConfirmModalVisible}
574574
headerMessage={getHeaderMessage()}
575-
headerContent={getHeaderContent()}
575+
headerContent={!isSmallScreenWidth && getHeaderContent()}
576576
onSelectRow={openMemberDetails}
577577
onCheckboxPress={(item) => toggleUser(item.accountID)}
578578
onSelectAll={() => toggleAllUsers(data)}
@@ -582,6 +582,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft,
582582
textInputRef={textInputRef}
583583
customListHeader={getCustomListHeader()}
584584
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
585+
listHeaderContent={isSmallScreenWidth ? <View style={[styles.pl5, styles.pr5]}>{getHeaderContent()}</View> : null}
585586
showScrollIndicator={false}
586587
/>
587588
</View>

src/pages/workspace/categories/WorkspaceCategoriesPage.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,24 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
243243

244244
const shouldShowEmptyState = !categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) && !isLoading;
245245

246+
const getHeaderText = () => (
247+
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
248+
{Object.keys(policy?.connections ?? {}).length > 0 ? (
249+
<Text>
250+
<Text style={[styles.textNormal, styles.colorMuted]}>{`${translate('workspace.categories.importedFromAccountingSoftware')} `}</Text>
251+
<TextLink
252+
style={[styles.textNormal, styles.link]}
253+
href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyId)}`}
254+
>
255+
{`${translate('workspace.accounting.qbo')} ${translate('workspace.accounting.settings')}`}
256+
</TextLink>
257+
</Text>
258+
) : (
259+
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.categories.subtitle')}</Text>
260+
)}
261+
</View>
262+
);
263+
246264
return (
247265
<AccessOrNotFoundWrapper
248266
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]}
@@ -274,21 +292,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
274292
danger
275293
/>
276294
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
277-
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
278-
{Object.keys(policy?.connections ?? {}).length > 0 ? (
279-
<Text>
280-
<Text style={[styles.textNormal, styles.colorMuted]}>{`${translate('workspace.categories.importedFromAccountingSoftware')} `}</Text>
281-
<TextLink
282-
style={[styles.textNormal, styles.link]}
283-
href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyId)}`}
284-
>
285-
{`${translate('workspace.accounting.qbo')} ${translate('workspace.accounting.settings')}`}
286-
</TextLink>
287-
</Text>
288-
) : (
289-
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.categories.subtitle')}</Text>
290-
)}
291-
</View>
295+
{!isSmallScreenWidth && getHeaderText()}
292296
{isLoading && (
293297
<ActivityIndicator
294298
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -315,6 +319,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
315319
onDismissError={dismissError}
316320
customListHeader={getCustomListHeader()}
317321
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
322+
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
318323
showScrollIndicator={false}
319324
/>
320325
)}

src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,12 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps)
262262
</View>
263263
);
264264

265+
const getHeaderText = () => (
266+
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
267+
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.distanceRates.centrallyManage')}</Text>
268+
</View>
269+
);
270+
265271
return (
266272
<AccessOrNotFoundWrapper
267273
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]}
@@ -282,9 +288,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps)
282288
{!isSmallScreenWidth && headerButtons}
283289
</HeaderWithBackButton>
284290
{isSmallScreenWidth && <View style={[styles.ph5]}>{headerButtons}</View>}
285-
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
286-
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.distanceRates.centrallyManage')}</Text>
287-
</View>
291+
{!isSmallScreenWidth && getHeaderText()}
288292
{isLoading && (
289293
<ActivityIndicator
290294
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -304,6 +308,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps)
304308
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
305309
customListHeader={getCustomListHeader()}
306310
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
311+
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
307312
showScrollIndicator={false}
308313
/>
309314
)}

src/pages/workspace/tags/WorkspaceTagsPage.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,24 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
269269
);
270270
};
271271

272+
const getHeaderText = () => (
273+
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
274+
{isConnectedToAccounting ? (
275+
<Text>
276+
<Text style={[styles.textNormal, styles.colorMuted]}>{`${translate('workspace.tags.importedFromAccountingSoftware')} `}</Text>
277+
<TextLink
278+
style={[styles.textNormal, styles.link]}
279+
href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyID)}`}
280+
>
281+
{`${translate('workspace.accounting.qbo')} ${translate('workspace.accounting.settings')}`}
282+
</TextLink>
283+
</Text>
284+
) : (
285+
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.tags.subtitle')}</Text>
286+
)}
287+
</View>
288+
);
289+
272290
return (
273291
<AccessOrNotFoundWrapper
274292
policyID={policyID}
@@ -289,6 +307,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
289307
>
290308
{!isSmallScreenWidth && getHeaderButtons()}
291309
</HeaderWithBackButton>
310+
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
292311
<ConfirmModal
293312
isVisible={isDeleteTagsConfirmModalVisible}
294313
onConfirm={deleteTags}
@@ -299,22 +318,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
299318
cancelText={translate('common.cancel')}
300319
danger
301320
/>
302-
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
303-
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
304-
{isConnectedToAccounting ? (
305-
<Text>
306-
<Text style={[styles.textNormal, styles.colorMuted]}>{`${translate('workspace.tags.importedFromAccountingSoftware')} `}</Text>
307-
<TextLink
308-
style={[styles.textNormal, styles.link]}
309-
href={`${environmentURL}/${ROUTES.POLICY_ACCOUNTING.getRoute(policyID)}`}
310-
>
311-
{`${translate('workspace.accounting.qbo')} ${translate('workspace.accounting.settings')}`}
312-
</TextLink>
313-
</Text>
314-
) : (
315-
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.tags.subtitle')}</Text>
316-
)}
317-
</View>
321+
{!isSmallScreenWidth && getHeaderText()}
318322
{isLoading && (
319323
<ActivityIndicator
320324
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -341,6 +345,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
341345
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
342346
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
343347
onDismissError={(item) => Policy.clearPolicyTagErrors(policyID, item.value)}
348+
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
344349
showScrollIndicator={false}
345350
/>
346351
)}

src/pages/workspace/taxes/WorkspaceTaxesPage.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ function WorkspaceTaxesPage({
234234
/>
235235
);
236236

237+
const getHeaderText = () => (
238+
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
239+
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.taxes.subtitle')}</Text>
240+
</View>
241+
);
242+
237243
return (
238244
<AccessOrNotFoundWrapper
239245
accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.PAID]}
@@ -253,12 +259,8 @@ function WorkspaceTaxesPage({
253259
>
254260
{!isSmallScreenWidth && headerButtons}
255261
</HeaderWithBackButton>
256-
257262
{isSmallScreenWidth && <View style={[styles.pl5, styles.pr5]}>{headerButtons}</View>}
258-
259-
<View style={[styles.ph5, styles.pb5, styles.pt3]}>
260-
<Text style={[styles.textNormal, styles.colorMuted]}>{translate('workspace.taxes.subtitle')}</Text>
261-
</View>
263+
{!isSmallScreenWidth && getHeaderText()}
262264
{isLoading && (
263265
<ActivityIndicator
264266
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
@@ -274,6 +276,7 @@ function WorkspaceTaxesPage({
274276
onSelectAll={toggleAllTaxes}
275277
ListItem={TableListItem}
276278
customListHeader={getCustomListHeader()}
279+
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
277280
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
278281
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
279282
onDismissError={(item) => (item.keyForList ? clearTaxRateError(policyID, item.keyForList, item.pendingAction) : undefined)}

src/styles/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4481,6 +4481,10 @@ const styles = (theme: ThemeColors) =>
44814481
borderRadius: 8,
44824482
},
44834483

4484+
selectionListStickyHeader: {
4485+
backgroundColor: theme.appBG,
4486+
},
4487+
44844488
draggableTopBar: {
44854489
height: 30,
44864490
width: '100%',

0 commit comments

Comments
 (0)