Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type SearchTableHeaderProps = {
sortOrder?: SortOrder;
onSortPress: (column: SearchSortBy, order: SortOrder) => void;
dateColumnSize: TableColumnSize;
postedColumnSize: TableColumnSize;
amountColumnSize: TableColumnSize;
taxAmountColumnSize: TableColumnSize;
shouldShowSorting: boolean;
Expand All @@ -27,6 +28,7 @@ function MoneyRequestReportTableHeader({
sortOrder,
onSortPress,
dateColumnSize,
postedColumnSize,
shouldShowSorting,
columns,
amountColumnSize,
Expand Down Expand Up @@ -86,6 +88,7 @@ function MoneyRequestReportTableHeader({
columns={orderedColumnConfig}
shouldShowColumn={shouldShowColumn}
dateColumnSize={dateColumnSize}
postedColumnSize={postedColumnSize}
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
shouldShowSorting={shouldShowSorting}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ type MoneyRequestReportTransactionItemProps = {
/** The size of the date column */
dateColumnSize: TableColumnSize;

/** The size of the posted column */
postedColumnSize: TableColumnSize;

/** The size of the amount column */
amountColumnSize: TableColumnSize;

Expand Down Expand Up @@ -122,6 +125,7 @@ function MoneyRequestReportTransactionItemBody({
handleLongPress,
columns,
dateColumnSize,
postedColumnSize,
amountColumnSize,
taxAmountColumnSize,
scrollToNewTransaction,
Expand Down Expand Up @@ -236,6 +240,7 @@ function MoneyRequestReportTransactionItemBody({
transactionThreadReportID={transactionThreadReportID}
isSelected={isSelected}
dateColumnSize={dateColumnSize}
postedColumnSize={postedColumnSize}
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
shouldShowTooltip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {compareValues, getColumnsToShow, getTableMinWidth, hasFlexColumn, isTran
import {getPendingSubmitFollowUpAction} from '@libs/telemetry/submitFollowUpAction';
import {transactionHasRBR} from '@libs/TransactionPreviewUtils';
import {getTransactionPendingAction, getVisibleTransactionViolations, isTransactionPendingDelete, shouldShowExpenseBreakdown} from '@libs/TransactionUtils';
import shouldShowTransactionPostedYear from '@libs/TransactionUtils/shouldShowTransactionPostedYear';
import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear';

import isReportOpenInSuperWideRHP from '@navigation/helpers/isReportOpenInSuperWideRHP';
Expand Down Expand Up @@ -571,14 +572,16 @@ function MoneyRequestReportTransactionList({
[navigateToTransactionThread, reportActions, sortedTransactions, report, visualOrderTransactionIDs],
);

const {amountColumnSize, dateColumnSize, taxAmountColumnSize} = useMemo(() => {
const {amountColumnSize, dateColumnSize, postedColumnSize, taxAmountColumnSize} = useMemo(() => {
const isAmountColumnWide = transactions.some((transaction) => isTransactionAmountTooLong(transaction));
const isTaxAmountColumnWide = transactions.some((transaction) => isTransactionTaxAmountTooLong(transaction));
const shouldShowYearForSomeTransaction = transactions.some((transaction) => shouldShowTransactionYear(transaction));
const shouldShowPostedYearForSomeTransaction = transactions.some((transaction) => shouldShowTransactionPostedYear(transaction));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Widen posted column when its cell renders a year

For report-layout rows with raw card posted dates such as 20260710, TransactionItemRowWide passes transactionItem.posted directly to DateCell; DateCell chooses the year format through DateUtils.doesDateBelongToAPastYear, whose new Date('YYYYMMDD') path yields NaN and therefore selects the year format even for current-year values. This new size predicate returns false for current-year posted dates, so those rows still render MMM d, yyyy in the normal 62px Posted column and remain truncated. Consider matching the renderer's predicate or normalizing posted dates before sizing/rendering.

Useful? React with 👍 / 👎.

return {
amountColumnSize: isAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
taxAmountColumnSize: isTaxAmountColumnWide ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
dateColumnSize: shouldShowYearForSomeTransaction ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,
postedColumnSize: shouldShowPostedYearForSomeTransaction ? CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE : CONST.SEARCH.TABLE_COLUMN_SIZES.NORMAL,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include wide Posted width in horizontal scroll sizing

When any posted date needs the wider 102px cell, this only widens the rendered header/rows; the report table still decides shouldScrollHorizontally and the ScrollView contentContainerStyle from getTableMinWidth(columnsToShow), which has no access to postedColumnSize and always budgets the POSTED column at the fixed narrow amount. On desktop widths where the old table width fits but the widened Posted column does not, the table will not enter the horizontal-scroll path (or the scroll content width will be too small), so the Posted column can still be clipped instead of becoming scrollable.

Useful? React with 👍 / 👎.

@mukhrr mukhrr Jul 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This looks like a pre-existing limitation of the date-column widening rather than something introduced here. getTableMinWidth (SearchUIUtils.ts:6256) budgets a fixed width per column — DATE +48, SUBMITTED/APPROVED/POSTED +72 — with no awareness of the wide 102px variant, so the same gap already applies to Date/Submitted/Approved/Exported, all of which widen to 102px today. In the report layout the Date column already widens (its dateColumnSize was wired before this PR) with the exact same discrepancy. This change just brings Posted to parity with its sibling date columns rather than creating a new scroll-sizing path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Making getTableMinWidth size-aware for the whole date-column family would fix it, but I'd prefer to handle that as a separate follow-up than fold it into this focused Posted fix.

};
}, [transactions]);

Expand Down Expand Up @@ -722,6 +725,7 @@ function MoneyRequestReportTransactionList({
handleOnPress={handleOnPress}
handleLongPress={handleLongPress}
dateColumnSize={dateColumnSize}
postedColumnSize={postedColumnSize}
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
scrollToNewTransaction={transaction.transactionID === newTransactions?.at(0)?.transactionID ? scrollToNewTransaction : undefined}
Expand Down Expand Up @@ -831,6 +835,7 @@ function MoneyRequestReportTransactionList({
shouldRemoveTotalColumnFlex={hasFlexColumn(columnsToShow)}
columns={columnsToShow}
dateColumnSize={dateColumnSize}
postedColumnSize={postedColumnSize}
amountColumnSize={amountColumnSize}
taxAmountColumnSize={taxAmountColumnSize}
onSortPress={(selectedSortBy, selectedSortOrder) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
isScanning,
isTimeRequest,
} from '@libs/TransactionUtils';
import getFormattedPostedDate from '@libs/TransactionUtils/getFormattedPostedDate';

import variables from '@styles/variables';

Expand Down Expand Up @@ -248,7 +249,7 @@ function TransactionItemRowWide({
style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.POSTED, {isPostedColumnWide})]}
>
<DateCell
date={transactionItem.posted ?? ''}
date={getFormattedPostedDate(transactionItem.posted)}
showTooltip={shouldShowTooltip}
isLargeScreenWidth
/>
Expand Down
28 changes: 10 additions & 18 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ import {
shouldShowAttendees as shouldShowAttendeesUtils,
shouldShowViolation,
} from './TransactionUtils';
import getFormattedPostedDate from './TransactionUtils/getFormattedPostedDate';
import shouldShowTransactionPostedYear from './TransactionUtils/shouldShowTransactionPostedYear';
import {isInvalidMerchantValue} from './ValidationUtils';

type ColumnSortMapping<T> = Partial<Record<SearchColumnType, keyof T | null>>;
Expand Down Expand Up @@ -1216,8 +1218,7 @@ function getTransactionItemCommonFormattedProperties(
const submitted = report?.submitted;
const approved = report?.approved;

// Posted date is in the YYYYMMDD format, so we format it to YYYY-MM-DD here since JS's Date constructor interprets it as an invalid date.
const posted = !transactionItem?.posted ? '' : `${transactionItem?.posted.slice(0, 4)}-${transactionItem?.posted.slice(4, 6)}-${transactionItem?.posted.slice(6, 8)}`;
const posted = getFormattedPostedDate(transactionItem?.posted);

return {
formattedFrom,
Expand Down Expand Up @@ -1526,11 +1527,9 @@ function shouldShowYear(
result.shouldShowYearApproved = true;
}

// Posted date is in the YYYYMMDD format, so we extract the year manually here since JS's Date constructor interprets it as an invalid date.
// Latch once true so a later current-year date can't reset it (the row renderer latches the same way).
if (!result.shouldShowYearPosted && item?.posted) {
const postedYear = parseInt(item.posted.slice(0, 4), 10);
result.shouldShowYearPosted = postedYear !== currentYear;
if (!result.shouldShowYearPosted && shouldShowTransactionPostedYear(item)) {
result.shouldShowYearPosted = true;
}
}

Expand Down Expand Up @@ -1559,11 +1558,9 @@ function shouldShowYear(
result.shouldShowYearApproved = true;
}

// Posted date is in the YYYYMMDD format, so we extract the year manually here since JS's Date constructor interprets it as an invalid date.
// Latch once true so a later current-year date can't reset it (the row renderer latches the same way).
if (!result.shouldShowYearPosted && item?.posted) {
const postedYear = parseInt(item.posted.slice(0, 4), 10);
result.shouldShowYearPosted = postedYear !== currentYear;
if (!result.shouldShowYearPosted && shouldShowTransactionPostedYear(item)) {
result.shouldShowYearPosted = true;
}

const exportedAction = item.reportID ? lastExportedActionByReportID.get(item.reportID) : undefined;
Expand Down Expand Up @@ -1760,7 +1757,6 @@ type PreprocessingContext = {
shouldShowAmountInWideColumn: boolean;
shouldShowTaxAmountInWideColumn: boolean;
hasDeletedTransaction: boolean;
currentYear: number;
};

function createPreprocessingContext(): PreprocessingContext {
Expand All @@ -1787,7 +1783,6 @@ function createPreprocessingContext(): PreprocessingContext {
shouldShowAmountInWideColumn: false,
shouldShowTaxAmountInWideColumn: false,
hasDeletedTransaction: false,
currentYear: new Date().getFullYear(),
};
}

Expand Down Expand Up @@ -1905,11 +1900,8 @@ function processTransactionEntry(ctx: PreprocessingContext, transaction: OnyxTyp
ctx.shouldShowYearApproved = true;
}

if (!ctx.shouldShowYearPosted && transaction?.posted) {
const postedYear = parseInt(transaction.posted.slice(0, 4), 10);
if (postedYear !== ctx.currentYear) {
ctx.shouldShowYearPosted = true;
}
if (!ctx.shouldShowYearPosted && shouldShowTransactionPostedYear(transaction)) {
ctx.shouldShowYearPosted = true;
}

// Optimistic: works when report-action keys precede transaction keys (common case).
Expand Down Expand Up @@ -1980,7 +1972,7 @@ function resolveExportedYearFlags(ctx: PreprocessingContext, data: OnyxTypes.Sea
* (key classification, violations, export/action maps, year flags, column indicators)
* needed by getTransactionsSections and getReportSections.
*/
function classifyAndPreprocess(data: OnyxTypes.SearchResults['data']): Omit<PreprocessingContext, 'allHoldReportActions' | 'currentYear'> {
function classifyAndPreprocess(data: OnyxTypes.SearchResults['data']): Omit<PreprocessingContext, 'allHoldReportActions'> {
const ctx = createPreprocessingContext();

for (const key of Object.keys(data)) {
Expand Down
11 changes: 11 additions & 0 deletions src/libs/TransactionUtils/getFormattedPostedDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
function getFormattedPostedDate(posted?: string): string {
if (!posted) {
return '';
}
if (!/^\d{8}$/.test(posted)) {
return posted;
}
return `${posted.slice(0, 4)}-${posted.slice(4, 6)}-${posted.slice(6, 8)}`;
}

export default getFormattedPostedDate;
15 changes: 15 additions & 0 deletions src/libs/TransactionUtils/shouldShowTransactionPostedYear.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import DateUtils from '@libs/DateUtils';

import type {OnyxInputOrEntry, Transaction} from '@src/types/onyx';

import getFormattedPostedDate from './getFormattedPostedDate';

function shouldShowTransactionPostedYear(transaction: OnyxInputOrEntry<Transaction>) {
const posted = transaction?.posted;
if (!posted) {
return false;
}
return DateUtils.doesDateBelongToAPastYear(getFormattedPostedDate(posted));
}

export default shouldShowTransactionPostedYear;
22 changes: 22 additions & 0 deletions tests/unit/getFormattedPostedDateTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import getFormattedPostedDate from '@libs/TransactionUtils/getFormattedPostedDate';

describe('getFormattedPostedDate', () => {
it('converts a raw YYYYMMDD card posted date to YYYY-MM-DD', () => {
expect(getFormattedPostedDate('20260710')).toBe('2026-07-10');
expect(getFormattedPostedDate('20251231')).toBe('2025-12-31');
});

it('passes an already-ISO date through unchanged (idempotent)', () => {
expect(getFormattedPostedDate('2026-07-10')).toBe('2026-07-10');
});

it('returns an empty string for empty/undefined input', () => {
expect(getFormattedPostedDate(undefined)).toBe('');
expect(getFormattedPostedDate('')).toBe('');
});

it('passes any non-YYYYMMDD string through unchanged', () => {
expect(getFormattedPostedDate('2026-7-1')).toBe('2026-7-1');
expect(getFormattedPostedDate('not-a-date')).toBe('not-a-date');
});
});
26 changes: 26 additions & 0 deletions tests/unit/shouldShowTransactionPostedYearTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import shouldShowTransactionPostedYear from '@libs/TransactionUtils/shouldShowTransactionPostedYear';

import createRandomTransaction from '../utils/collections/transaction';

const currentYear = new Date().getFullYear();
const transactionWithPosted = (posted?: string) => ({...createRandomTransaction(0), posted});

describe('shouldShowTransactionPostedYear', () => {
it('returns true when the raw YYYYMMDD posted date is in a past year', () => {
expect(shouldShowTransactionPostedYear(transactionWithPosted(`${currentYear - 1}0701`))).toBe(true);
});

it('returns false when the raw YYYYMMDD posted date is in the current year', () => {
expect(shouldShowTransactionPostedYear(transactionWithPosted(`${currentYear}0701`))).toBe(false);
});

it('returns false when there is no posted date', () => {
expect(shouldShowTransactionPostedYear(transactionWithPosted(undefined))).toBe(false);
expect(shouldShowTransactionPostedYear(transactionWithPosted(''))).toBe(false);
});

it('returns false for a nullish transaction', () => {
expect(shouldShowTransactionPostedYear(undefined)).toBe(false);
expect(shouldShowTransactionPostedYear(null)).toBe(false);
});
});
Loading