Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: IOU - Disabled tag is greyed in list but disabled category is shown bold in list. #37397

Merged
merged 40 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0f63c8a
fix: IOU - Disabled tag is greyed in list but disabled category is sh…
Krishna2323 Feb 28, 2024
e8a8243
Merge branch 'main' of https://github.com/Krishna2323/App into krishn…
Krishna2323 Mar 4, 2024
22a6f20
resolve conflicts.
Krishna2323 Mar 4, 2024
061cdd1
apply changes to new CategoryPicker.tsx
Krishna2323 Mar 4, 2024
ea142f9
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Mar 8, 2024
d3df9e9
fix: disabled seleted category not shown in list when searching.
Krishna2323 Mar 9, 2024
d78eebd
revert all previous changes.
Krishna2323 Mar 9, 2024
0a3cd60
minor spacing fix
Krishna2323 Mar 9, 2024
a6c6b51
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Mar 9, 2024
1a3c16e
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Mar 11, 2024
a4485ff
fix jest tests fails.
Krishna2323 Mar 11, 2024
e0918c8
fix const names.
Krishna2323 Mar 11, 2024
dd51736
Update Gemfile.lock
Krishna2323 Mar 11, 2024
d8f81d8
Update Gemfile.lock
Krishna2323 Mar 11, 2024
9f9669d
Update Gemfile.lock
Krishna2323 Mar 11, 2024
cdd87dd
fix: jest tests.
Krishna2323 Mar 11, 2024
ace92e2
Merge branch 'krishna2323/issue/36485' of https://github.com/Krishna2…
Krishna2323 Mar 11, 2024
6262dcd
revert all changes.
Krishna2323 Mar 31, 2024
7e86e62
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Mar 31, 2024
db7f8b1
show selected categories when searching.
Krishna2323 Mar 31, 2024
686c587
update TagPicker to use SelectionList.
Krishna2323 Mar 31, 2024
75c5382
remove redundant code.
Krishna2323 Mar 31, 2024
d52eb44
added disabled styles without disabling the select/unselect functiona…
Krishna2323 Mar 31, 2024
2810b86
minor changes.
Krishna2323 Mar 31, 2024
f7470d6
fix: tag not shown as selected.
Krishna2323 Mar 31, 2024
f0dd8d5
fix: merge conflicts.
Krishna2323 Apr 20, 2024
ef9358d
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Apr 20, 2024
d7e3e5c
remove redundant checks.
Krishna2323 Apr 20, 2024
8551719
make list item deselectable if disabled but has been selected.
Krishna2323 Apr 20, 2024
d126d63
make list item deselectable if disabled but has been selected for tag…
Krishna2323 Apr 20, 2024
4ebf594
fix: seleceted category & tax not showing in search results.
Krishna2323 Apr 20, 2024
cd8ad67
Merge branch 'main' into krishna2323/issue/36485
Krishna2323 Apr 23, 2024
11a8759
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Apr 24, 2024
8fd696e
fix: dubplicate taxes shown & selected disabled tax not showing when …
Krishna2323 Apr 24, 2024
c03ded6
fix: Tag - duplicate selected options shown and seleceted disbaled op…
Krishna2323 Apr 24, 2024
89249fa
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 Apr 26, 2024
d58f5fb
remove redundant eslint comment.
Krishna2323 Apr 29, 2024
775e24b
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 May 1, 2024
a7f8a29
fix: Jest Unit Tests.
Krishna2323 May 1, 2024
b05ee69
Merge branch 'Expensify:main' into krishna2323/issue/36485
Krishna2323 May 4, 2024
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
1 change: 0 additions & 1 deletion src/components/CategoryPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ function CategoryPicker({selectedCategory, policyCategories, policyRecentlyUsedC
return [
{
name: selectedCategory,
enabled: true,
accountID: undefined,
isSelected: true,
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/SelectionList/BaseListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function BaseListItem<TItem extends ListItem>({
}
onSelectRow(item);
}}
disabled={isDisabled}
disabled={isDisabled && !item.isSelected}
accessibilityLabel={item.text ?? ''}
role={CONST.ROLE.BUTTON}
hoverDimmingValue={1}
Expand Down
1 change: 1 addition & 0 deletions src/components/TagPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {PolicyTag, PolicyTagList, PolicyTags, RecentlyUsedTags} from '@src/
type SelectedTagOption = {
name: string;
enabled: boolean;
isSelected?: boolean;
accountID: number | undefined;
};

Expand Down
6 changes: 3 additions & 3 deletions src/components/TaxPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ function TaxPicker({selectedTaxRate = '', policy, insets, onSubmit}: TaxPickerPr

return [
{
name: selectedTaxRate,
enabled: true,
modifiedName: selectedTaxRate,
isDisabled: false,
accountID: null,
},
];
}, [selectedTaxRate]);

const sections = useMemo(() => OptionsListUtils.getTaxRatesSection(taxRates, selectedOptions as OptionsListUtils.Category[], searchValue), [taxRates, searchValue, selectedOptions]);
const sections = useMemo(() => OptionsListUtils.getTaxRatesSection(taxRates, selectedOptions as OptionsListUtils.Tax[], searchValue), [taxRates, searchValue, selectedOptions]);

const headerMessage = OptionsListUtils.getHeaderMessageForNonUserList(sections[0].data.length > 0, searchValue);

Expand Down
113 changes: 64 additions & 49 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ type Category = {
isSelected?: boolean;
};

type Tax = {
modifiedName: string;
isSelected?: boolean;
isDisabled?: boolean;
};

type Hierarchy = Record<string, Category & {[key: string]: Hierarchy & Category}>;

type GetOptionsConfig = {
Expand Down Expand Up @@ -1013,12 +1019,21 @@ function getCategoryListSections(
): CategoryTreeSection[] {
const sortedCategories = sortCategories(categories);
const enabledCategories = Object.values(sortedCategories).filter((category) => category.enabled);

const enabledCategoriesNames = enabledCategories.map((category) => category.name);
const selectedOptionsWithDisabledState: Category[] = [];
const categorySections: CategoryTreeSection[] = [];
const numberOfEnabledCategories = enabledCategories.length;

selectedOptions.forEach((option) => {
if (enabledCategoriesNames.includes(option.name)) {
selectedOptionsWithDisabledState.push({...option, isSelected: true, enabled: true});
return;
}
selectedOptionsWithDisabledState.push({...option, isSelected: true, enabled: false});
});

if (numberOfEnabledCategories === 0 && selectedOptions.length > 0) {
const data = getCategoryOptionTree(selectedOptions, true);
const data = getCategoryOptionTree(selectedOptionsWithDisabledState, true);
categorySections.push({
// "Selected" section
title: '',
Expand All @@ -1031,9 +1046,10 @@ function getCategoryListSections(
}

if (searchInputValue) {
const categoriesForSearch = [...selectedOptionsWithDisabledState, ...enabledCategories];
const searchCategories: Category[] = [];

enabledCategories.forEach((category) => {
categoriesForSearch.forEach((category) => {
if (!category.name.toLowerCase().includes(searchInputValue.toLowerCase())) {
return;
}
Expand All @@ -1056,7 +1072,7 @@ function getCategoryListSections(
}

if (selectedOptions.length > 0) {
const data = getCategoryOptionTree(selectedOptions, true);
const data = getCategoryOptionTree(selectedOptionsWithDisabledState, true);
categorySections.push({
// "Selected" section
title: '',
Expand Down Expand Up @@ -1147,34 +1163,42 @@ function getTagListSections(
const tagSections = [];
const sortedTags = sortTags(tags) as PolicyTag[];
const selectedOptionNames = selectedOptions.map((selectedOption) => selectedOption.name);
const enabledTags = [...selectedOptions, ...sortedTags.filter((tag) => tag.enabled && !selectedOptionNames.includes(tag.name))];
const enabledTags = sortedTags.filter((tag) => tag.enabled);
const enabledTagsNames = enabledTags.map((tag) => tag.name);
const enabledTagsWithoutSelectedOptions = enabledTags.filter((tag) => !selectedOptionNames.includes(tag.name));
const selectedTagsWithDisabledState: SelectedTagOption[] = [];
const numberOfTags = enabledTags.length;

selectedOptions.forEach((tag) => {
if (enabledTagsNames.includes(tag.name)) {
selectedTagsWithDisabledState.push({...tag, enabled: true});
return;
}
selectedTagsWithDisabledState.push({...tag, enabled: false});
});

// If all tags are disabled but there's a previously selected tag, show only the selected tag
if (numberOfTags === 0 && selectedOptions.length > 0) {
const selectedTagOptions = selectedOptions.map((option) => ({
name: option.name,
// Should be marked as enabled to be able to be de-selected
enabled: true,
}));
tagSections.push({
// "Selected" section
title: '',
shouldShow: false,
data: getTagsOptions(selectedTagOptions, selectedOptions),
data: getTagsOptions(selectedTagsWithDisabledState, selectedOptions),
});

return tagSections;
}

if (searchInputValue) {
const searchTags = enabledTags.filter((tag) => PolicyUtils.getCleanedTagName(tag.name.toLowerCase()).includes(searchInputValue.toLowerCase()));
const enabledSearchTags = enabledTagsWithoutSelectedOptions.filter((tag) => PolicyUtils.getCleanedTagName(tag.name.toLowerCase()).includes(searchInputValue.toLowerCase()));
const selectedSearchTags = selectedTagsWithDisabledState.filter((tag) => PolicyUtils.getCleanedTagName(tag.name.toLowerCase()).includes(searchInputValue.toLowerCase()));
const tagsForSearch = [...selectedSearchTags, ...enabledSearchTags];

tagSections.push({
// "Search" section
title: '',
shouldShow: true,
data: getTagsOptions(searchTags, selectedOptions),
data: getTagsOptions(tagsForSearch, selectedOptions),
});

return tagSections;
Expand All @@ -1185,7 +1209,7 @@ function getTagListSections(
// "All" section when items amount less than the threshold
title: '',
shouldShow: false,
data: getTagsOptions(enabledTags, selectedOptions),
data: getTagsOptions([...selectedTagsWithDisabledState, ...enabledTagsWithoutSelectedOptions], selectedOptions),
});

return tagSections;
Expand All @@ -1197,20 +1221,13 @@ function getTagListSections(
return !!tagObject?.enabled && !selectedOptionNames.includes(recentlyUsedTag);
})
.map((tag) => ({name: tag, enabled: true}));
const filteredTags = enabledTags.filter((tag) => !selectedOptionNames.includes(tag.name));

if (selectedOptions.length) {
const selectedTagOptions = selectedOptions.map((option) => ({
name: option.name,
// Should be marked as enabled to be able to unselect even though the selected category is disabled
enabled: true,
}));

tagSections.push({
// "Selected" section
title: '',
shouldShow: true,
data: getTagsOptions(selectedTagOptions, selectedOptions),
data: getTagsOptions(selectedTagsWithDisabledState, selectedOptions),
});
}

Expand All @@ -1229,7 +1246,7 @@ function getTagListSections(
// "All" section when items amount more than the threshold
title: Localize.translateLocal('common.all'),
shouldShow: true,
data: getTagsOptions(filteredTags, selectedOptions),
data: getTagsOptions(enabledTagsWithoutSelectedOptions, selectedOptions),
});

return tagSections;
Expand Down Expand Up @@ -1352,46 +1369,56 @@ function getTaxRatesOptions(taxRates: Array<Partial<TaxRate>>): TaxRatesOption[]
tooltipText: taxRate.modifiedName,
isDisabled: taxRate.isDisabled,
data: taxRate,
isSelected: taxRate.isSelected,
}));
}

/**
* Builds the section list for tax rates
*/
function getTaxRatesSection(taxRates: TaxRatesWithDefault | undefined, selectedOptions: Category[], searchInputValue: string): TaxSection[] {
function getTaxRatesSection(taxRates: TaxRatesWithDefault | undefined, selectedOptions: Tax[], searchInputValue: string): TaxSection[] {
const policyRatesSections = [];

const taxes = transformedTaxRates(taxRates);

const sortedTaxRates = sortTaxRates(taxes);
const selectedOptionNames = selectedOptions.map((selectedOption) => selectedOption.modifiedName);
const enabledTaxRates = sortedTaxRates.filter((taxRate) => !taxRate.isDisabled);
const enabledTaxRatesNames = enabledTaxRates.map((tax) => tax.modifiedName);
const enabledTaxRatesWithoutSelectedOptions = enabledTaxRates.filter((tax) => tax.modifiedName && !selectedOptionNames.includes(tax.modifiedName));
const selectedTaxRateWithDisabledState: Tax[] = [];
const numberOfTaxRates = enabledTaxRates.length;

selectedOptions.forEach((tax) => {
if (enabledTaxRatesNames.includes(tax.modifiedName)) {
selectedTaxRateWithDisabledState.push({...tax, isDisabled: false, isSelected: true});
return;
}
selectedTaxRateWithDisabledState.push({...tax, isDisabled: true, isSelected: true});
});

// If all tax are disabled but there's a previously selected tag, show only the selected tag
if (numberOfTaxRates === 0 && selectedOptions.length > 0) {
const selectedTaxRateOptions = selectedOptions.map((option) => ({
modifiedName: option.name,
// Should be marked as enabled to be able to be de-selected
isDisabled: false,
}));
policyRatesSections.push({
// "Selected" sectiong
title: '',
shouldShow: false,
data: getTaxRatesOptions(selectedTaxRateOptions),
data: getTaxRatesOptions(selectedTaxRateWithDisabledState),
});

return policyRatesSections;
}

if (searchInputValue) {
const searchTaxRates = enabledTaxRates.filter((taxRate) => taxRate.modifiedName?.toLowerCase().includes(searchInputValue.toLowerCase()));
const enabledSearchTaxRates = enabledTaxRatesWithoutSelectedOptions.filter((taxRate) => taxRate.modifiedName?.toLowerCase().includes(searchInputValue.toLowerCase()));
const selectedSearchTags = selectedTaxRateWithDisabledState.filter((taxRate) => taxRate.modifiedName?.toLowerCase().includes(searchInputValue.toLowerCase()));
const taxesForSearch = [...selectedSearchTags, ...enabledSearchTaxRates];

policyRatesSections.push({
// "Search" section
title: '',
shouldShow: true,
data: getTaxRatesOptions(searchTaxRates),
data: getTaxRatesOptions(taxesForSearch),
});

return policyRatesSections;
Expand All @@ -1402,38 +1429,26 @@ function getTaxRatesSection(taxRates: TaxRatesWithDefault | undefined, selectedO
// "All" section when items amount less than the threshold
title: '',
shouldShow: false,
data: getTaxRatesOptions(enabledTaxRates),
data: getTaxRatesOptions([...selectedTaxRateWithDisabledState, ...enabledTaxRatesWithoutSelectedOptions]),
});

return policyRatesSections;
}

const selectedOptionNames = selectedOptions.map((selectedOption) => selectedOption.name);
const filteredTaxRates = enabledTaxRates.filter((taxRate) => taxRate.modifiedName && !selectedOptionNames.includes(taxRate.modifiedName));

if (selectedOptions.length > 0) {
const selectedTaxRatesOptions = selectedOptions.map((option) => {
const taxRateObject = Object.values(taxes).find((taxRate) => taxRate.modifiedName === option.name);

return {
modifiedName: option.name,
isDisabled: !!taxRateObject?.isDisabled,
};
});

policyRatesSections.push({
// "Selected" section
title: '',
shouldShow: true,
data: getTaxRatesOptions(selectedTaxRatesOptions),
data: getTaxRatesOptions(selectedTaxRateWithDisabledState),
});
}

policyRatesSections.push({
// "All" section when number of items are more than the threshold
title: '',
shouldShow: true,
data: getTaxRatesOptions(filteredTaxRates),
data: getTaxRatesOptions(enabledTaxRatesWithoutSelectedOptions),
});

return policyRatesSections;
Expand Down Expand Up @@ -1687,7 +1702,7 @@ function getOptions(
}

if (includeTaxRates) {
const taxRatesOptions = getTaxRatesSection(taxRates, selectedOptions as Category[], searchInputValue);
const taxRatesOptions = getTaxRatesSection(taxRates, selectedOptions as Tax[], searchInputValue);

return {
recentReports: [],
Expand Down Expand Up @@ -2427,4 +2442,4 @@ export {
getFirstKeyForList,
};

export type {MemberForList, CategorySection, CategoryTreeSection, Options, OptionList, SearchOption, PayeePersonalDetails, Category, TaxRatesOption, Option, OptionTree};
export type {MemberForList, CategorySection, CategoryTreeSection, Options, OptionList, SearchOption, PayeePersonalDetails, Category, Tax, TaxRatesOption, Option, OptionTree};
3 changes: 3 additions & 0 deletions src/types/onyx/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ type TaxRate = OnyxCommon.OnyxValueWithOfflineFeedback<{
/** Indicates if the tax rate is disabled. */
isDisabled?: boolean;

/** Indicates if the tax rate is selected. */
isSelected?: boolean;

/** An error message to display to the user */
errors?: OnyxCommon.Errors;

Expand Down
12 changes: 8 additions & 4 deletions tests/unit/OptionsListUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1067,8 +1067,8 @@ describe('OptionsListUtils', () => {
keyForList: 'Medical',
searchText: 'Medical',
tooltipText: 'Medical',
isDisabled: false,
isSelected: false,
isDisabled: true,
isSelected: true,
},
],
},
Expand Down Expand Up @@ -1236,8 +1236,8 @@ describe('OptionsListUtils', () => {
keyForList: 'Medical',
searchText: 'Medical',
tooltipText: 'Medical',
isDisabled: false,
isSelected: false,
isDisabled: true,
isSelected: true,
},
],
},
Expand Down Expand Up @@ -2587,6 +2587,7 @@ describe('OptionsListUtils', () => {
searchText: 'Tax exempt 1 (0%) • Default',
tooltipText: 'Tax exempt 1 (0%) • Default',
isDisabled: undefined,
isSelected: undefined,
// creates a data option.
data: {
name: 'Tax exempt 1',
Expand All @@ -2601,6 +2602,7 @@ describe('OptionsListUtils', () => {
searchText: 'Tax option 3 (5%)',
tooltipText: 'Tax option 3 (5%)',
isDisabled: undefined,
isSelected: undefined,
data: {
name: 'Tax option 3',
code: 'CODE3',
Expand All @@ -2614,6 +2616,7 @@ describe('OptionsListUtils', () => {
searchText: 'Tax rate 2 (3%)',
tooltipText: 'Tax rate 2 (3%)',
isDisabled: undefined,
isSelected: undefined,
data: {
name: 'Tax rate 2',
code: 'CODE2',
Expand All @@ -2637,6 +2640,7 @@ describe('OptionsListUtils', () => {
searchText: 'Tax rate 2 (3%)',
tooltipText: 'Tax rate 2 (3%)',
isDisabled: undefined,
isSelected: undefined,
data: {
name: 'Tax rate 2',
code: 'CODE2',
Expand Down
Loading