From 6b5129392aafa8fb7d6bb7c33326bd2c4a974349 Mon Sep 17 00:00:00 2001 From: "Eskalifer1 (via MelvinBot)" Date: Mon, 5 Oct 2026 16:02:11 +0000 Subject: [PATCH 1/2] Show list report fields when disabledOptions is shorter than values Co-authored-by: Eskalifer1 --- src/libs/ReportUtils.ts | 11 +++++- .../workspace/fields/FieldsSettingsPage.tsx | 4 +- tests/unit/ReportUtilsTest.ts | 38 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0a5f6a790044..3b74622d08b8 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -14318,8 +14318,16 @@ function isWorkspaceMemberLeavingWorkspaceRoom(report: OnyxEntry, isPoli return (report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED || hasAccessPolicyExpenseChat) && isPolicyEmployee; } +/** + * Checks whether a list report field has at least one enabled value. + * A value without a matching `disabledOptions` entry is treated as enabled. + */ +function hasEnabledListValue(reportField: PolicyReportField): boolean { + return reportField.values.some((_, index) => !reportField.disabledOptions.at(index)); +} + function shouldHideSingleReportField(reportField: PolicyReportField) { - const hasEnableOption = reportField.type !== CONST.REPORT_FIELD_TYPES.LIST || reportField.disabledOptions.some((option) => !option); + const hasEnableOption = reportField.type !== CONST.REPORT_FIELD_TYPES.LIST || hasEnabledListValue(reportField); return isReportFieldOfTypeTitle(reportField) || !hasEnableOption; } @@ -14783,6 +14791,7 @@ export { hasReportBeenForwardedSinceLastSubmit, hasAutomatedExpensifyAccountIDs, hasEmptyReportsForPolicy, + hasEnabledListValue, hasHeldExpenses, hasIOUWaitingOnCurrentUserBankAccount, hasOnlyHeldExpenses, diff --git a/src/pages/workspace/fields/FieldsSettingsPage.tsx b/src/pages/workspace/fields/FieldsSettingsPage.tsx index df78ada9a13a..d30eabdf8825 100644 --- a/src/pages/workspace/fields/FieldsSettingsPage.tsx +++ b/src/pages/workspace/fields/FieldsSettingsPage.tsx @@ -12,7 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import type {PolicyFeature} from '@libs/PolicyUtils'; -import {getReportFieldKey} from '@libs/ReportUtils'; +import {getReportFieldKey, hasEnabledListValue} from '@libs/ReportUtils'; import {getReportFieldInitialValue, getReportFieldTypeTranslationKey, isReportFieldImportedFromIntegration, isReportFieldTargetValid} from '@libs/WorkspaceReportFieldUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; @@ -75,7 +75,7 @@ function FieldsSettingsPage({ const isDateFieldType = reportField.type === CONST.REPORT_FIELD_TYPES.DATE; const isListFieldType = reportField.type === CONST.REPORT_FIELD_TYPES.LIST; - const isListFieldEmpty = isListFieldType && reportField.disabledOptions.filter((disabledListValue) => !disabledListValue).length <= 0; + const isListFieldEmpty = isListFieldType && !hasEnabledListValue(reportField); const listValues = Object.values(policy?.fieldList?.[reportFieldKey]?.values ?? {})?.sort(localeCompare); const deleteReportFieldAndHideModal = () => { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 68878937b51c..0269ddfb229d 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -20906,6 +20906,7 @@ describe('ReportUtils', () => { it('should return false if list report field has some options enabled', () => { const reportField = createMock({ type: CONST.REPORT_FIELD_TYPES.LIST, + values: ['A', 'B', 'C'], disabledOptions: [false, true, false], }); expect(shouldHideSingleReportField(reportField)).toBe(false); @@ -20913,10 +20914,47 @@ describe('ReportUtils', () => { it('should return false if all items in list report field are disabled', () => { const reportField = createMock({ type: CONST.REPORT_FIELD_TYPES.LIST, + values: ['A', 'B', 'C'], disabledOptions: [true, true, true], }); expect(shouldHideSingleReportField(reportField)).toBe(true); }); + it('should return false if list report field has values but an empty disabledOptions array', () => { + // Given a list field not created in NewDot, where the backend sends an empty disabledOptions array + const reportField = createMock({ + type: CONST.REPORT_FIELD_TYPES.LIST, + values: ['A', 'B'], + disabledOptions: [], + }); + + // When checking whether the field should be hidden + // Then it is visible, because values without a disabledOptions entry are treated as enabled + expect(shouldHideSingleReportField(reportField)).toBe(false); + }); + it('should return false if disabledOptions is shorter than values and the missing entries are enabled', () => { + // Given a list field where only the first value has a disabledOptions entry + const reportField = createMock({ + type: CONST.REPORT_FIELD_TYPES.LIST, + values: ['A', 'B', 'C'], + disabledOptions: [true], + }); + + // When checking whether the field should be hidden + // Then it is visible, because the values without an entry are treated as enabled + expect(shouldHideSingleReportField(reportField)).toBe(false); + }); + it('should return true if every value of a list report field is explicitly disabled', () => { + // Given a list field where every value is disabled + const reportField = createMock({ + type: CONST.REPORT_FIELD_TYPES.LIST, + values: ['A', 'B'], + disabledOptions: [true, true], + }); + + // When checking whether the field should be hidden + // Then it is hidden, because no value can be selected + expect(shouldHideSingleReportField(reportField)).toBe(true); + }); }); describe('shouldDisplayReportFields', () => { it('returns true for an invoice when the receiver policy does not include the invoice fields feature flag', () => { From 99f828d86006e781a06062118a40c0835dd4f00c Mon Sep 17 00:00:00 2001 From: "Eskalifer1 (via MelvinBot)" Date: Mon, 5 Oct 2026 16:59:16 +0000 Subject: [PATCH 2/2] Explain empty disabledOptions in hasEnabledListValue and use it in FieldsInitialValuePage Co-authored-by: Eskalifer1 --- src/libs/ReportUtils.ts | 4 +++- src/pages/workspace/fields/FieldsInitialValuePage.tsx | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3b74622d08b8..369e3ec2dd04 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -14320,7 +14320,9 @@ function isWorkspaceMemberLeavingWorkspaceRoom(report: OnyxEntry, isPoli /** * Checks whether a list report field has at least one enabled value. - * A value without a matching `disabledOptions` entry is treated as enabled. + * A value without a matching `disabledOptions` entry is treated as enabled, because fields created outside NewDot + * can arrive with an empty `disabledOptions` array even when `values` has entries. Iterate over `values` rather than + * calling `disabledOptions.some(...)`, which would return false for those fields and hide them. */ function hasEnabledListValue(reportField: PolicyReportField): boolean { return reportField.values.some((_, index) => !reportField.disabledOptions.at(index)); diff --git a/src/pages/workspace/fields/FieldsInitialValuePage.tsx b/src/pages/workspace/fields/FieldsInitialValuePage.tsx index 9564a512f4cb..b5ab10c08958 100644 --- a/src/pages/workspace/fields/FieldsInitialValuePage.tsx +++ b/src/pages/workspace/fields/FieldsInitialValuePage.tsx @@ -12,7 +12,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {hasCircularReferences} from '@libs/Formula'; import Navigation from '@libs/Navigation/Navigation'; -import {getReportFieldKey} from '@libs/ReportUtils'; +import {getReportFieldKey, hasEnabledListValue} from '@libs/ReportUtils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import {getReportFieldInitialValue, getReportFieldsForTarget, getUnsupportedReportFieldFormulaParts, isReportFieldTargetValid} from '@libs/WorkspaceReportFieldUtils'; @@ -49,7 +49,7 @@ function FieldsInitialValuePage({policy, policyID, reportFieldID, featureName, e const reportField = policy?.fieldList?.[getReportFieldKey(reportFieldID)] ?? null; const isInvoiceField = reportField?.target === CONST.REPORT_FIELD_TARGETS.INVOICE; - const availableListValuesLength = (reportField?.disabledOptions ?? []).filter((disabledListValue) => !disabledListValue).length; + const hasAvailableListValues = !!reportField && hasEnabledListValue(reportField); const currentInitialValue = getReportFieldInitialValue(reportField, translate); const [initialValue, setInitialValue] = useState(currentInitialValue); @@ -91,7 +91,7 @@ function FieldsInitialValuePage({policy, policyID, reportFieldID, featureName, e } } - if (reportField?.type === CONST.REPORT_FIELD_TYPES.LIST && availableListValuesLength > 0 && !isRequiredFulfilled(formInitialValue)) { + if (reportField?.type === CONST.REPORT_FIELD_TYPES.LIST && hasAvailableListValues && !isRequiredFulfilled(formInitialValue)) { errors[INPUT_IDS.INITIAL_VALUE] = translate( isInvoiceField ? 'workspace.invoiceFields.invoiceFieldInitialValueRequiredError' : 'workspace.reportFields.reportFieldInitialValueRequiredError', ); @@ -99,7 +99,7 @@ function FieldsInitialValuePage({policy, policyID, reportFieldID, featureName, e return errors; }, - [availableListValuesLength, expectedTarget, isInvoiceField, reportField?.name, reportField?.type, policy?.fieldList, translate], + [hasAvailableListValues, expectedTarget, isInvoiceField, reportField?.name, reportField?.type, policy?.fieldList, translate], ); if (!reportField || !isReportFieldTargetValid(reportField, expectedTarget)) {