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
13 changes: 12 additions & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14318,8 +14318,18 @@ function isWorkspaceMemberLeavingWorkspaceRoom(report: OnyxEntry<Report>, 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, 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));
}

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;
}
Expand Down Expand Up @@ -14783,6 +14793,7 @@ export {
hasReportBeenForwardedSinceLastSubmit,
hasAutomatedExpensifyAccountIDs,
hasEmptyReportsForPolicy,
hasEnabledListValue,
hasHeldExpenses,
hasIOUWaitingOnCurrentUserBankAccount,
hasOnlyHeldExpenses,
Expand Down
8 changes: 4 additions & 4 deletions src/pages/workspace/fields/FieldsInitialValuePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -91,15 +91,15 @@ 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',
);
}

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)) {
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/fields/FieldsSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 = () => {
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/ReportUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20906,17 +20906,55 @@ describe('ReportUtils', () => {
it('should return false if list report field has some options enabled', () => {
const reportField = createMock<PolicyReportField>({
type: CONST.REPORT_FIELD_TYPES.LIST,
values: ['A', 'B', 'C'],
disabledOptions: [false, true, false],
});
expect(shouldHideSingleReportField(reportField)).toBe(false);
});
it('should return false if all items in list report field are disabled', () => {
const reportField = createMock<PolicyReportField>({
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<PolicyReportField>({
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<PolicyReportField>({
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<PolicyReportField>({
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', () => {
Expand Down
Loading