-
Notifications
You must be signed in to change notification settings - Fork 4k
Add group-by:tag #80638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add group-by:tag #80638
Changes from all commits
7c7ce11
df6cf5b
48cd069
63d343a
6f6b063
c631554
a3633b0
edf63fb
5934a03
00ff34e
e97d969
b1f0d53
184e126
c48cc6f
5e449d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import Checkbox from '@components/Checkbox'; | ||
| import type {SearchColumnType} from '@components/Search/types'; | ||
| import type {ListItem, TransactionTagGroupListItemType} from '@components/SelectionListWithSections/types'; | ||
| import TextWithTooltip from '@components/TextWithTooltip'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
| import useStyleUtils from '@hooks/useStyleUtils'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import CONST from '@src/CONST'; | ||
| import ExpandCollapseArrowButton from './ExpandCollapseArrowButton'; | ||
| import TextCell from './TextCell'; | ||
| import TotalCell from './TotalCell'; | ||
|
|
||
| type TagListItemHeaderProps<TItem extends ListItem> = { | ||
| /** The tag currently being looked at */ | ||
| tag: TransactionTagGroupListItemType; | ||
|
|
||
| /** Callback to fire when a checkbox is pressed */ | ||
| onCheckboxPress?: (item: TItem) => void; | ||
|
|
||
| /** Whether this section items disabled for selection */ | ||
| isDisabled?: boolean | null; | ||
|
|
||
| /** Whether selecting multiple transactions at once is allowed */ | ||
| canSelectMultiple: boolean | undefined; | ||
|
|
||
| /** Whether all transactions are selected */ | ||
| isSelectAllChecked?: boolean; | ||
|
|
||
| /** Whether only some transactions are selected */ | ||
| isIndeterminate?: boolean; | ||
|
|
||
| /** Callback for when the down arrow is clicked */ | ||
| onDownArrowClick?: () => void; | ||
|
|
||
| /** Whether the down arrow is expanded */ | ||
| isExpanded?: boolean; | ||
|
|
||
| /** The visible columns for the header */ | ||
| columns?: SearchColumnType[]; | ||
| }; | ||
|
|
||
| function TagListItemHeader<TItem extends ListItem>({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❌ CONSISTENCY-3 (docs)The entire Current duplication:
Fix: Extract the common logic into a shared generic component: // Create a generic GroupListItemHeader component
function GroupListItemHeader<TItem extends ListItem, TGroupItem>({
groupItem,
displayName,
columnComponents,
// ... other common props
}) {
// Shared implementation for all group headers
}
// Then TagListItemHeader becomes:
function TagListItemHeader(props) {
const displayName = /* tag-specific logic */;
const columnComponents = /* tag-specific columns */;
return <GroupListItemHeader {...props} displayName={displayName} columnComponents={columnComponents} />;
}This follows the DRY principle and makes it easier to maintain consistent behavior across all group header types. Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency. |
||
| tag: tagItem, | ||
| onCheckboxPress, | ||
| isDisabled, | ||
| canSelectMultiple, | ||
| isSelectAllChecked, | ||
| isIndeterminate, | ||
| isExpanded, | ||
| onDownArrowClick, | ||
| columns, | ||
| }: TagListItemHeaderProps<TItem>) { | ||
| const styles = useThemeStyles(); | ||
| const StyleUtils = useStyleUtils(); | ||
| const {isLargeScreenWidth} = useResponsiveLayout(); | ||
| const {translate} = useLocalize(); | ||
|
|
||
| // formattedTag is already translated to "No tag" for empty values in SearchUIUtils | ||
| const tagName = tagItem.formattedTag ?? tagItem.tag ?? ''; | ||
|
|
||
| const columnComponents = { | ||
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_TAG]: ( | ||
| <View | ||
| key={CONST.SEARCH.TABLE_COLUMNS.GROUP_TAG} | ||
| style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TAG)} | ||
| > | ||
| <View style={[styles.gap1, styles.flexShrink1]}> | ||
| <TextWithTooltip | ||
| text={tagName} | ||
| style={[styles.optionDisplayName, styles.sidebarLinkTextBold, styles.pre, styles.fontWeightNormal]} | ||
| /> | ||
| </View> | ||
| </View> | ||
| ), | ||
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES]: ( | ||
| <View | ||
| key={CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES} | ||
| style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.EXPENSES)} | ||
| > | ||
| <TextCell text={String(tagItem.count)} /> | ||
| </View> | ||
| ), | ||
| [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: ( | ||
| <View | ||
| key={CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL} | ||
| style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TOTAL, false, false, false, false, false, false, false, true)} | ||
| > | ||
| <TotalCell | ||
| total={tagItem.total} | ||
| currency={tagItem.currency} | ||
| /> | ||
| </View> | ||
| ), | ||
| }; | ||
|
|
||
| return ( | ||
| <View> | ||
| <View style={[styles.pv1Half, styles.pl3, styles.flexRow, styles.alignItemsCenter, isLargeScreenWidth ? styles.gap3 : styles.justifyContentStart]}> | ||
| <View style={[styles.flexRow, styles.alignItemsCenter, styles.mnh40, styles.flex1, styles.gap3]}> | ||
| {!!canSelectMultiple && ( | ||
| <Checkbox | ||
| onPress={() => onCheckboxPress?.(tagItem as unknown as TItem)} | ||
| isChecked={isSelectAllChecked} | ||
| isIndeterminate={isIndeterminate} | ||
| disabled={!!isDisabled || tagItem.isDisabledCheckbox} | ||
| accessibilityLabel={translate('common.select')} | ||
| style={isLargeScreenWidth && styles.mr1} | ||
| /> | ||
| )} | ||
| {!isLargeScreenWidth && ( | ||
| <View style={[styles.flexRow, styles.flex1, styles.gap3]}> | ||
| <View style={[styles.gap1, styles.flexShrink1]}> | ||
| <TextWithTooltip | ||
| text={tagName} | ||
| style={[styles.optionDisplayName, styles.sidebarLinkTextBold, styles.pre, styles.fontWeightNormal]} | ||
| /> | ||
| </View> | ||
| </View> | ||
| )} | ||
| {isLargeScreenWidth && columns?.map((column) => columnComponents[column as keyof typeof columnComponents])} | ||
| </View> | ||
| {!isLargeScreenWidth && ( | ||
| <View style={[styles.flexShrink0, styles.ml2, styles.mr3, styles.gap1]}> | ||
| <TotalCell | ||
| total={tagItem.total} | ||
| currency={tagItem.currency} | ||
| /> | ||
| {!!onDownArrowClick && ( | ||
| <ExpandCollapseArrowButton | ||
| isExpanded={isExpanded} | ||
| onPress={onDownArrowClick} | ||
| /> | ||
| )} | ||
| </View> | ||
| )} | ||
| </View> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| export default TagListItemHeader; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌ CONSISTENCY-3 (docs)
This tag value normalization logic is duplicated in
src/libs/SearchUIUtils.ts(in thegetTagSectionsfunction). The same logic appears twice:Fix: Extract this logic into a shared utility function:
Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.