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
11 changes: 9 additions & 2 deletions src/libs/actions/Policy/Tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import type {OnyxData} from '@src/types/onyx/Request';

let allPolicyTags: OnyxCollection<PolicyTagLists> = {};
Onyx.connect({

Check warning on line 38 in src/libs/actions/Policy/Tag.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -490,8 +490,15 @@
});
}

function clearPolicyTagListErrorField(policyID: string, tagListIndex: number, errorField: string) {
const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex);
type ClearPolicyTagListErrorFieldProps = {
policyID: string;
tagListIndex: number;
errorField: string;
policyTags: OnyxEntry<PolicyTagLists>;
};

function clearPolicyTagListErrorField({policyID, tagListIndex, errorField, policyTags}: ClearPolicyTagListErrorFieldProps) {
const policyTag = PolicyUtils.getTagLists(policyTags ?? {})?.at(tagListIndex);

if (!policyTag) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
}}
pendingAction={currentPolicyTag.pendingFields?.required}
errors={currentPolicyTag?.errorFields?.required ?? undefined}
onCloseError={() => clearPolicyTagListErrorField(policyID, route.params.orderWeight, 'required')}
onCloseError={() => clearPolicyTagListErrorField({policyID, tagListIndex: route.params.orderWeight, errorField: 'required', policyTags})}
disabled={!currentPolicyTag?.required && !Object.values(currentPolicyTag?.tags ?? {}).some((tag) => tag.enabled)}
showLockIcon={isMakingLastRequiredTagListOptional(policy, policyTags, [currentPolicyTag])}
/>
Expand Down
132 changes: 132 additions & 0 deletions tests/actions/PolicyTagTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager';
import {
buildOptimisticPolicyRecentlyUsedTags,
clearPolicyTagErrors,
clearPolicyTagListErrorField,
createPolicyTag,
deletePolicyTags,
renamePolicyTag,
Expand Down Expand Up @@ -1022,6 +1023,137 @@ describe('actions/Policy', () => {
});
});

describe('ClearPolicyTagListErrorField', () => {
it('should clear specific error field from tag list', async () => {
// Given a policy with a tag list that has multiple error fields
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 2);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
errorFields: {
name: {genericError: 'Name error'},
required: {genericError: 'Required error'},
maxTagsSelected: {genericError: 'Max tags error'},
},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When clearing only the 'required' error field from the tag list
clearPolicyTagListErrorField({policyID: fakePolicy.id, tagListIndex: 0, errorField: 'required', policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then only the 'required' error field should be cleared while other error fields remain
expect(updatedPolicyTags?.[tagListName]).toBeDefined();
expect(updatedPolicyTags?.[tagListName].errorFields?.required).toBeUndefined();
expect(updatedPolicyTags?.[tagListName].errorFields?.name).toEqual({genericError: 'Name error'});
expect(updatedPolicyTags?.[tagListName].errorFields?.maxTagsSelected).toEqual({genericError: 'Max tags error'});
});

it('should not modify Onyx data when tag list does not exist', async () => {
// Given a policy with no tag lists
const fakePolicy = createRandomPolicy(0);
const fakePolicyTags = {};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When attempting to clear an error field from a non-existent tag list
expect(() => {
clearPolicyTagListErrorField({policyID: fakePolicy.id, tagListIndex: 0, errorField: 'required', policyTags: fakePolicyTags});
}).not.toThrow();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the policy tags should remain unchanged because the tag list does not exist
expect(updatedPolicyTags).toEqual(fakePolicyTags);
});

it('should not modify Onyx data when tag list has no name', async () => {
// Given a policy with a tag list that has an empty name and error fields
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 1);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
name: '',
errorFields: {
required: {genericError: 'This error should not be cleared'},
name: {genericError: 'This error should also remain'},
},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When attempting to clear an error field from a tag list with no name
expect(() => {
clearPolicyTagListErrorField({policyID: fakePolicy.id, tagListIndex: 0, errorField: 'required', policyTags: fakePolicyTags});
}).not.toThrow();

await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the error fields should remain unchanged because the tag list name is empty
expect(updatedPolicyTags?.[tagListName].errorFields?.required).toEqual({genericError: 'This error should not be cleared'});
expect(updatedPolicyTags?.[tagListName].errorFields?.name).toEqual({genericError: 'This error should also remain'});
expect(updatedPolicyTags?.[tagListName].name).toBe('');
});

it('should work with data from useOnyx hook', async () => {
// Given a policy with a tag list that has error fields and is accessed via useOnyx hook
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 1);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
errorFields: {
required: {genericError: 'Required field error'},
name: {genericError: 'Name field error'},
},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

const {result} = renderHook(() => useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`));

await waitFor(() => {
expect(result.current[0]).toBeDefined();
});

// When clearing the 'name' error field using data from the useOnyx hook
clearPolicyTagListErrorField({policyID: fakePolicy.id, tagListIndex: 0, errorField: 'name', policyTags: result.current[0]});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then only the 'name' error field should be cleared while the 'required' error field remains
expect(updatedPolicyTags?.[tagListName].errorFields?.name).toBeUndefined();
expect(updatedPolicyTags?.[tagListName].errorFields?.required).toEqual({genericError: 'Required field error'});
});
});

describe('buildOptimisticPolicyRecentlyUsedTags', () => {
it('should return empty object when transactionTags is undefined', () => {
const result = buildOptimisticPolicyRecentlyUsedTags({
Expand Down
Loading