Tracking GH: https://github.com/Expensify/Expensify/issues/418655
Design Doc: https://docs.google.com/document/d/1_p8qZXVKG-s32i9GKqVjCLEcEREBUEgYcxk3KYHXMVg/edit?usp=sharing
From the Design Doc:
Expense: Selecting Dependent Tags
There is no change for Independent Multi-level Tags ; we display them as they are imported on the Confirmation page. In this section, we need to configure the display of dependent tags, For adding dependent tags to expenses, child tag will reveal themselves as parent tags get selected:
- Update logic to reveal child tag when parent tag gets selected
In MoneyRequestConfirmationListFooter while mapping the tags available we need to check if the current tags are dependent tags and if so then we need to update the logic of displaying child tags, they will reveal as parent tags get selected (Note that if we only have 1 child tag then it will get selected by default similar to how do currently do on OldDot.): \
const hasDependentTags = useMemo(
() => PolicyUtils.hasDependentTags(policy, policyTags),
[policy, policyTags]
);
// Use useAnimatedHighlightStyle from [here](https://github.com/Expensify/App/blob/9586baffc8c90230f9affa3d98fa4e4899f58359/src/hooks/useAnimatedHighlightStyle/index.ts#L49), to highlight the newly row added
const animatedHighlightStyle = useAnimatedHighlightStyle({
borderRadius: variables.componentBorderRadius,
shouldHighlight: item?.shouldAnimateInHighlight ?? false,
highlightColor: theme.messageHighlightBG, // For yellow highlight
backgroundColor: theme.highlightBG,
});
...policyTagLists.map(({ name, required, tags }, index) => {
const isTagRequired = required ?? false;
// We should always show the Parent (index 0) tag regardless of whether it is
// selected or not.
// For other cases, we should show the current tag only when the parent tag is
// selected
const shouldShowDependentTag =
index === 0
? true
: !!TransactionUtils.getTagForDisplay(transaction, index - 1);
// then if we are using dependent tag the use the logic of dependent while showing children tags
const shouldShow = hasDependentTags
? shouldShowDependentTag
: shouldShowTags &&
(!isMultilevelTags || OptionsListUtils.hasEnabledOptions(tags));
return {
item: (
<MenuItemWithTopDescription
style={animatedHighlightStyle}
...props
/>
),
shouldShow,
isSupplementary: !isTagRequired,
};
});,
- Note: We will temporarily highlight the MenuItem when a new dependent tag row is added.
- Clear all children tag values when parent tags are updated
Now for cases where we change the lower-order index tags (Parent tags), we need to clear all the children tags (Similar to OldDot). For that, we will make use of the existing insertTagIntoTransactionTagsString util, but we would have to introduce two new props: hasDependentTags and policyTagListsLength, this is because we want to clear all the children tag values up to the total tagIndex in the given policyTagList. So first, introduce the new props:
function insertTagIntoTransactionTagsString(
transactionTags: string,
tag: string,
tagIndex: number,
hasDependentTag?: boolean,
policyTagListsLength?: number
): string {
const tagArray = TransactionUtils.getTagArrayFromName(transactionTags);
tagArray[tagIndex] = tag;
// If hasDependentTag is true, clear tags greater than tagIndex up to policyTagListsLength
if (hasDependentTag) {
for (let i = tagIndex + 1; i < policyTagListsLength; i++) {
tagArray[i] = ""; // Clear the dependent tags
}
}
while (tagArray.length > 0 && !tagArray.at(-1)) {
tagArray.pop();
}
return tagArray.map((tagItem) => tagItem.trim()).join(CONST.COLON);
}
In IOURequestStepTag, we will pass these two new props:
const updateTag = (selectedTag: Partial<ReportUtils.OptionData>) => {
const isSelectedTag = selectedTag.searchText === tag;
const searchText = selectedTag.searchText ?? "";
// here we pass the new props
const updatedTag = IOUUtils.insertTagIntoTransactionTagsString(
transactionTag,
isSelectedTag ? "" : searchText,
tagListIndex,
hasDependentTags,
policyTagLists.length
);
..........
};
These changes will ensure that child tag push inputs reveal themselves as parent tags are selected and that child tags get cleared when the parent tag is changed.
- Filter tag list based on the parentFilter for dependent multi-level tags
The final part of this would be to filter the children tags according to the selection of the parent tags, for that we need to make a change to the base TagPicker component and we also need to introduce a new TransactionUtils named as getTagUptoIndex, we need to match the parentTagsFilter with the currently selected parent-child tag value: \
function getTagUptoIndex(
transaction: OnyxInputOrEntry<Transaction>,
tagIndex?: number
): string {
if (tagIndex !== undefined && transaction?.tag) {
const tagsArray = getTagArrayFromName(transaction?.tag ?? "");
// Get tags from start up to (but not including) tagIndex of the current child tag and join them back into a string
return tagsArray.slice(0, tagIndex).join(",");
}
return transaction?.tag ?? "";
}
This will give us a string array which we will then use in TagPicker to match the regex of parentTagsFilter, this allows us to only show the dependent tags corresponding to currently selected parent tag, So now, when we calculate the enabledTags here, \
const currentlySelectedTag = TransactionUtils.getTagUptoIndex(
currentTransaction,
tagListIndex
);
const enabledTags: PolicyTags | Array<PolicyTag | SelectedTagOption> =
useMemo(() => {
if (!shouldShowDisabledAndSelectedOption) {
// we should only filter according to the parentTagsFilter when we have dependent tag and we are not on the first parent tag
return hasDependentTags && !!currentlySelectedTag
? Object.values(policyTagList.tags).filter((tag) => {
// Make sure to return the comparison result
return (
tag.rules?.parentTagsFilter ===
`^${currentlySelectedTag.replace(",", "\\:")}$`
);
})
: policyTagList.tags;
}
........
}, [selectedOptions, policyTagList, shouldShowDisabledAndSelectedOption]);
The regex ^${currentlySelectedTag.replace(",", "\\:")}$ regex will take the array currentlySelectedTag as input, and then it will try to match theparentTagsFilterValue, this way we will only show the children tags for the currently selected parent tag:
\
POC video can be found here. \
Issue Owner
Current Issue Owner: @CortneyOfstad
Tracking GH: https://github.com/Expensify/Expensify/issues/418655
Design Doc: https://docs.google.com/document/d/1_p8qZXVKG-s32i9GKqVjCLEcEREBUEgYcxk3KYHXMVg/edit?usp=sharing
From the Design Doc:
Expense: Selecting Dependent Tags
There is no change for Independent Multi-level Tags ; we display them as they are imported on the Confirmation page. In this section, we need to configure the display of dependent tags, For adding dependent tags to expenses, child tag will reveal themselves as parent tags get selected:
In MoneyRequestConfirmationListFooter while mapping the tags available we need to check if the current tags are dependent tags and if so then we need to update the logic of displaying child tags, they will reveal as parent tags get selected (Note that if we only have 1 child tag then it will get selected by default similar to how do currently do on OldDot.): \
Now for cases where we change the lower-order index tags (Parent tags), we need to clear all the children tags (Similar to OldDot). For that, we will make use of the existing insertTagIntoTransactionTagsString util, but we would have to introduce two new props: hasDependentTags and policyTagListsLength, this is because we want to clear all the children tag values up to the total tagIndex in the given policyTagList. So first, introduce the new props:
In IOURequestStepTag, we will pass these two new props:
These changes will ensure that child tag push inputs reveal themselves as parent tags are selected and that child tags get cleared when the parent tag is changed.
The final part of this would be to filter the children tags according to the selection of the parent tags, for that we need to make a change to the base TagPicker component and we also need to introduce a new TransactionUtils named as getTagUptoIndex, we need to match the parentTagsFilter with the currently selected parent-child tag value: \
This will give us a string array which we will then use in TagPicker to match the regex of parentTagsFilter, this allows us to only show the dependent tags corresponding to currently selected parent tag, So now, when we calculate the enabledTags here, \
The regex
^${currentlySelectedTag.replace(",", "\\:")}$regex will take the arraycurrentlySelectedTagas input, and then it will try to match theparentTagsFilterValue, this way we will only show the children tags for the currently selected parent tag:\
POC video can be found here. \
Issue Owner
Current Issue Owner: @CortneyOfstad