From 237c9942eee056962020060a6d971f5627295367 Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins <4316490+frank69rq@users.noreply.github.com> Date: Sat, 18 Mar 2023 20:30:01 +0000 Subject: [PATCH] :recycle: refactor Nordigen and Category autocomplete usage (#784) The final `Autocomplete` refactors. After this is merged what's remaining is to do extensive testing and address the bugs in https://github.com/actualbudget/actual/issues/773 This PR moves `Nordigen` autocomplete to the new one without using a feature flag. IMO this is a safe change given the simple nature of the Nordigen autocomplete component. --- .../desktop-client/src/components/Modals.js | 1 + .../components/accounts/TransactionsTable.js | 6 +- .../src/components/util/GenericInput.js | 6 +- .../src/components/CategoryAutocomplete.js | 91 +++++++++++++ .../src/components/budget/index.js | 9 +- .../budget/rollover/BudgetSummary.js | 22 +++- .../budget/rollover/TransferTooltip.js | 8 +- .../budget/rollover/rollover-components.js | 20 ++- .../src/components/modals/EditField.js | 7 +- .../components/modals/NordigenExternalMsg.js | 73 ++--------- .../src/components/modals/countries.js | 124 +++++++++--------- upcoming-release-notes/784.md | 6 + 12 files changed, 238 insertions(+), 135 deletions(-) create mode 100644 packages/loot-design/src/components/CategoryAutocomplete.js create mode 100644 upcoming-release-notes/784.md diff --git a/packages/desktop-client/src/components/Modals.js b/packages/desktop-client/src/components/Modals.js index 894c3407..eae0f072 100644 --- a/packages/desktop-client/src/components/Modals.js +++ b/packages/desktop-client/src/components/Modals.js @@ -288,6 +288,7 @@ function Modals({ modalProps={modalProps} month={options.month} actions={actions} + isNewAutocompleteEnabled={isNewAutocompleteEnabled} isGoalTemplatesEnabled={isGoalTemplatesEnabled} /> diff --git a/packages/desktop-client/src/components/accounts/TransactionsTable.js b/packages/desktop-client/src/components/accounts/TransactionsTable.js index 9a8980c8..ee6f85fe 100644 --- a/packages/desktop-client/src/components/accounts/TransactionsTable.js +++ b/packages/desktop-client/src/components/accounts/TransactionsTable.js @@ -37,7 +37,8 @@ import { titleFirst, } from 'loot-core/src/shared/util'; import LegacyAccountAutocomplete from 'loot-design/src/components/AccountAutocomplete'; -import CategoryAutocomplete from 'loot-design/src/components/CategorySelect'; +import NewCategoryAutocomplete from 'loot-design/src/components/CategoryAutocomplete'; +import LegacyCategoryAutocomplete from 'loot-design/src/components/CategorySelect'; import { View, Text, Tooltip, Button } from 'loot-design/src/components/common'; import DateSelect from 'loot-design/src/components/DateSelect'; import NewAccountAutocomplete from 'loot-design/src/components/NewAccountAutocomplete'; @@ -533,6 +534,9 @@ export const Transaction = React.memo(function Transaction(props) { const AccountAutocomplete = isNewAutocompleteEnabled ? NewAccountAutocomplete : LegacyAccountAutocomplete; + const CategoryAutocomplete = isNewAutocompleteEnabled + ? NewCategoryAutocomplete + : LegacyCategoryAutocomplete; let dispatchSelected = useSelectedDispatch(); diff --git a/packages/desktop-client/src/components/util/GenericInput.js b/packages/desktop-client/src/components/util/GenericInput.js index a6f6d88f..b167af4d 100644 --- a/packages/desktop-client/src/components/util/GenericInput.js +++ b/packages/desktop-client/src/components/util/GenericInput.js @@ -4,7 +4,8 @@ import { useSelector } from 'react-redux'; import { getMonthYearFormat } from 'loot-core/src/shared/months'; import LegacyAccountAutocomplete from 'loot-design/src/components/AccountAutocomplete'; import LegacyAutocomplete from 'loot-design/src/components/Autocomplete'; -import CategoryAutocomplete from 'loot-design/src/components/CategorySelect'; +import NewCategoryAutocomplete from 'loot-design/src/components/CategoryAutocomplete'; +import LegacyCategoryAutocomplete from 'loot-design/src/components/CategorySelect'; import { View, Input } from 'loot-design/src/components/common'; import DateSelect from 'loot-design/src/components/DateSelect'; import { Checkbox } from 'loot-design/src/components/forms'; @@ -33,6 +34,9 @@ export default function GenericInput({ const AccountAutocomplete = isNewAutocompleteEnabled ? NewAccountAutocomplete : LegacyAccountAutocomplete; + const CategoryAutocomplete = isNewAutocompleteEnabled + ? NewCategoryAutocomplete + : LegacyCategoryAutocomplete; let { payees, accounts, categoryGroups, dateFormat } = useSelector(state => { return { diff --git a/packages/loot-design/src/components/CategoryAutocomplete.js b/packages/loot-design/src/components/CategoryAutocomplete.js new file mode 100644 index 00000000..77c8a70e --- /dev/null +++ b/packages/loot-design/src/components/CategoryAutocomplete.js @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; +import { components as SelectComponents } from 'react-select'; + +import { colors } from '../style'; +import Split from '../svg/v0/Split'; + +import { View } from './common'; +import Autocomplete from './NewAutocomplete'; + +const SPLIT_TRANSACTION_KEY = 'split'; + +export default function CategoryAutocomplete({ + value, + categoryGroups, + showSplitOption = false, + multi = false, + onSplit, + ...props +}) { + const options = useMemo(() => { + const suggestions = categoryGroups.map(group => ({ + label: group.name, + options: group.categories.map(categ => ({ + value: categ.id, + label: categ.name, + })), + })); + + if (showSplitOption) { + suggestions.unshift({ + value: SPLIT_TRANSACTION_KEY, + label: SPLIT_TRANSACTION_KEY, + }); + } + + return suggestions; + }, [categoryGroups, showSplitOption]); + + const allOptions = useMemo( + () => + options.reduce( + (carry, { options }) => [...carry, ...(options || [])], + [], + ), + [options], + ); + + return ( + value.includes(item.value)) + : allOptions.find(item => item.value === value) + } + isMulti={multi} + components={{ + Option, + }} + {...props} + /> + ); +} + +function Option(props) { + if (props.value === SPLIT_TRANSACTION_KEY) { + return ( + + + + Split Transaction + + + ); + } + return ; +} diff --git a/packages/loot-design/src/components/budget/index.js b/packages/loot-design/src/components/budget/index.js index 76b6115d..7bc0f739 100644 --- a/packages/loot-design/src/components/budget/index.js +++ b/packages/loot-design/src/components/budget/index.js @@ -1,4 +1,5 @@ import React, { useContext, useState, useMemo } from 'react'; +import { connect } from 'react-redux'; import * as monthUtils from 'loot-core/src/shared/months'; @@ -729,7 +730,11 @@ function ExpenseGroup({ ); } -function ExpenseCategory({ +const ExpenseCategory = connect(state => ({ + isNewAutocompleteEnabled: state.prefs.local['flags.newAutocomplete'], +}))(ExpenseCategoryInternal); + +function ExpenseCategoryInternal({ cat, budgetArray, editingCell, @@ -743,6 +748,7 @@ function ExpenseCategory({ onShowActivity, onDragChange, onReorder, + isNewAutocompleteEnabled, }) { let dragging = dragState && dragState.item === cat; @@ -801,6 +807,7 @@ function ExpenseCategory({ onEdit: onEditMonth, onBudgetAction, onShowActivity, + isNewAutocompleteEnabled, }} /> diff --git a/packages/loot-design/src/components/budget/rollover/BudgetSummary.js b/packages/loot-design/src/components/budget/rollover/BudgetSummary.js index 2aa757e6..57a2408e 100644 --- a/packages/loot-design/src/components/budget/rollover/BudgetSummary.js +++ b/packages/loot-design/src/components/budget/rollover/BudgetSummary.js @@ -134,7 +134,13 @@ function TotalsList({ prevMonthName, collapsed }) { ); } -function ToBudget({ month, prevMonthName, collapsed, onBudgetAction }) { +function ToBudget({ + month, + prevMonthName, + collapsed, + onBudgetAction, + isNewAutocompleteEnabled, +}) { return ( {node => { @@ -231,6 +237,7 @@ function ToBudget({ month, prevMonthName, collapsed, onBudgetAction }) { category, }); }} + isNewAutocompleteEnabled={isNewAutocompleteEnabled} /> )} @@ -243,7 +250,11 @@ function ToBudget({ month, prevMonthName, collapsed, onBudgetAction }) { ); } -export function BudgetSummary({ month, isGoalTemplatesEnabled }) { +export function BudgetSummary({ + month, + isGoalTemplatesEnabled, + isNewAutocompleteEnabled, +}) { let { currentMonth, summaryCollapsed: collapsed, @@ -405,13 +416,18 @@ export function BudgetSummary({ month, isGoalTemplatesEnabled }) { prevMonthName={prevMonthName} month={month} onBudgetAction={onBudgetAction} + isNewAutocompleteEnabled={isNewAutocompleteEnabled} /> ) : ( <> - + )} diff --git a/packages/loot-design/src/components/budget/rollover/TransferTooltip.js b/packages/loot-design/src/components/budget/rollover/TransferTooltip.js index 897419b7..8a22460f 100644 --- a/packages/loot-design/src/components/budget/rollover/TransferTooltip.js +++ b/packages/loot-design/src/components/budget/rollover/TransferTooltip.js @@ -3,7 +3,8 @@ import React, { useState, useContext, useEffect } from 'react'; import evalArithmetic from 'loot-core/src/shared/arithmetic'; import { integerToCurrency, amountToInteger } from 'loot-core/src/shared/util'; -import CategoryAutocomplete from '../../CategorySelect'; +import NewCategoryAutocomplete from '../../CategoryAutocomplete'; +import LegacyCategoryAutocomplete from '../../CategorySelect'; import { View, Button, Tooltip, InitialFocus, Input } from '../../common'; import NamespaceContext from '../../spreadsheet/NamespaceContext'; import SpreadsheetContext from '../../spreadsheet/SpreadsheetContext'; @@ -16,7 +17,12 @@ export default function TransferTooltip({ tooltipProps, onSubmit, onClose, + isNewAutocompleteEnabled, }) { + const CategoryAutocomplete = isNewAutocompleteEnabled + ? NewCategoryAutocomplete + : LegacyCategoryAutocomplete; + let spreadsheet = useContext(SpreadsheetContext); let sheetName = useContext(NamespaceContext); let categoryGroups = useContext(CategoryGroupsContext); diff --git a/packages/loot-design/src/components/budget/rollover/rollover-components.js b/packages/loot-design/src/components/budget/rollover/rollover-components.js index 559bbf80..333f0ef5 100644 --- a/packages/loot-design/src/components/budget/rollover/rollover-components.js +++ b/packages/loot-design/src/components/budget/rollover/rollover-components.js @@ -5,7 +5,8 @@ import evalArithmetic from 'loot-core/src/shared/arithmetic'; import { integerToCurrency, amountToInteger } from 'loot-core/src/shared/util'; import { styles, colors } from '../../../style'; -import CategoryAutocomplete from '../../CategorySelect'; +import NewCategoryAutocomplete from '../../CategoryAutocomplete'; +import LegacyCategoryAutocomplete from '../../CategorySelect'; import { View, Text, @@ -37,6 +38,7 @@ function CoverTooltip({ tooltipProps, onSubmit, onClose, + isNewAutocompleteEnabled, }) { let categoryGroups = useContext(CategoryGroupsContext); categoryGroups = addToBeBudgetedGroup( @@ -51,6 +53,10 @@ function CoverTooltip({ } } + const CategoryAutocomplete = isNewAutocompleteEnabled + ? NewCategoryAutocomplete + : LegacyCategoryAutocomplete; + return ( )} @@ -173,6 +186,7 @@ function BalanceTooltip({ categoryId, tooltip, monthIndex, onBudgetAction }) { from: fromCategory, }); }} + isNewAutocompleteEnabled={isNewAutocompleteEnabled} /> )} @@ -292,6 +306,7 @@ export const ExpenseCategoryMonth = React.memo(function ExpenseCategoryMonth({ onEdit, onBudgetAction, onShowActivity, + isNewAutocompleteEnabled, }) { let borderColor = colors.border; let balanceTooltip = useTooltip(); @@ -385,6 +400,7 @@ export const ExpenseCategoryMonth = React.memo(function ExpenseCategoryMonth({ tooltip={balanceTooltip} monthIndex={monthIndex} onBudgetAction={onBudgetAction} + isNewAutocompleteEnabled={isNewAutocompleteEnabled} /> )} diff --git a/packages/loot-design/src/components/modals/EditField.js b/packages/loot-design/src/components/modals/EditField.js index 44d1ff40..53a4f6ac 100644 --- a/packages/loot-design/src/components/modals/EditField.js +++ b/packages/loot-design/src/components/modals/EditField.js @@ -9,7 +9,8 @@ import { amountToInteger } from 'loot-core/src/shared/util'; import { colors } from '../../style'; import LegacyAccountAutocomplete from '../AccountAutocomplete'; -import CategoryAutocomplete from '../CategorySelect'; +import NewCategoryAutocomplete from '../CategoryAutocomplete'; +import LegacyCategoryAutocomplete from '../CategorySelect'; import { View, Modal, Input } from '../common'; import DateSelect from '../DateSelect'; import { SectionLabel } from '../forms'; @@ -56,6 +57,10 @@ function EditField({ ? NewAccountAutocomplete : LegacyAccountAutocomplete; + const CategoryAutocomplete = isNewAutocompleteEnabled + ? NewCategoryAutocomplete + : LegacyCategoryAutocomplete; + switch (name) { case 'date': { let today = currentDay(); diff --git a/packages/loot-design/src/components/modals/NordigenExternalMsg.js b/packages/loot-design/src/components/modals/NordigenExternalMsg.js index ae659b9b..e71be87b 100644 --- a/packages/loot-design/src/components/modals/NordigenExternalMsg.js +++ b/packages/loot-design/src/components/modals/NordigenExternalMsg.js @@ -5,9 +5,9 @@ import { send } from 'loot-core/src/platform/client/fetch'; import { colors } from '../../style'; import AnimatedLoading from '../../svg/AnimatedLoading'; import { Error } from '../alerts'; -import Autocomplete from '../Autocomplete'; import { View, Modal, Button, P } from '../common'; import { FormField, FormLabel } from '../forms'; +import Autocomplete from '../NewAutocomplete'; import { COUNTRY_OPTIONS } from './countries'; @@ -27,7 +27,7 @@ function useAvailableBanks(country) { const results = await send('nordigen-get-banks', country); - setBanks(results); + setBanks(results.map(bank => ({ value: bank.id, label: bank.name }))); setIsLoading(false); } @@ -124,22 +124,12 @@ export default function NordigenExternalMsg({ ( - - )} + value={COUNTRY_OPTIONS.find(({ value }) => value === country)} + inputId="country-field" + placeholder="(please select)" /> @@ -150,22 +140,12 @@ export default function NordigenExternalMsg({ ( - - )} + value={bankOptions.find(({ value }) => value === institutionId)} + inputId="bank-field" + placeholder="(please select)" /> ))} @@ -255,36 +235,3 @@ export default function NordigenExternalMsg({ ); } - -export function ItemList({ items, getItemProps, highlightedIndex }) { - return ( - - {items.map((item, idx) => ( -
- {item.name} -
- ))} -
- ); -} diff --git a/packages/loot-design/src/components/modals/countries.js b/packages/loot-design/src/components/modals/countries.js index 91de2930..9277ec95 100644 --- a/packages/loot-design/src/components/modals/countries.js +++ b/packages/loot-design/src/components/modals/countries.js @@ -1,126 +1,126 @@ export const COUNTRY_OPTIONS = [ { - id: 'AT', - name: 'Austria', + value: 'AT', + label: 'Austria', }, { - id: 'BE', - name: 'Belgium', + value: 'BE', + label: 'Belgium', }, { - id: 'BG', - name: 'Bulgaria', + value: 'BG', + label: 'Bulgaria', }, { - id: 'HR', - name: 'Croatia', + value: 'HR', + label: 'Croatia', }, { - id: 'CY', - name: 'Cyprus', + value: 'CY', + label: 'Cyprus', }, { - id: 'CZ', - name: 'Czechia', + value: 'CZ', + label: 'Czechia', }, { - id: 'DK', - name: 'Denmark', + value: 'DK', + label: 'Denmark', }, { - id: 'EE', - name: 'Estonia', + value: 'EE', + label: 'Estonia', }, { - id: 'FI', - name: 'Finland', + value: 'FI', + label: 'Finland', }, { - id: 'FR', - name: 'France', + value: 'FR', + label: 'France', }, { - id: 'DE', - name: 'Germany', + value: 'DE', + label: 'Germany', }, { - id: 'GR', - name: 'Greece', + value: 'GR', + label: 'Greece', }, { - id: 'HU', - name: 'Hungary', + value: 'HU', + label: 'Hungary', }, { - id: 'IS', - name: 'Iceland', + value: 'IS', + label: 'Iceland', }, { - id: 'IE', - name: 'Ireland', + value: 'IE', + label: 'Ireland', }, { - id: 'IT', - name: 'Italy', + value: 'IT', + label: 'Italy', }, { - id: 'LV', - name: 'Latvia', + value: 'LV', + label: 'Latvia', }, { - id: 'LI', - name: 'Liechtenstein', + value: 'LI', + label: 'Liechtenstein', }, { - id: 'LT', - name: 'Lithuania', + value: 'LT', + label: 'Lithuania', }, { - id: 'LU', - name: 'Luxembourg', + value: 'LU', + label: 'Luxembourg', }, { - id: 'MT', - name: 'Malta', + value: 'MT', + label: 'Malta', }, { - id: 'NL', - name: 'Netherlands', + value: 'NL', + label: 'Netherlands', }, { - id: 'NO', - name: 'Norway', + value: 'NO', + label: 'Norway', }, { - id: 'PL', - name: 'Poland', + value: 'PL', + label: 'Poland', }, { - id: 'PT', - name: 'Portugal', + value: 'PT', + label: 'Portugal', }, { - id: 'RO', - name: 'Romania', + value: 'RO', + label: 'Romania', }, { - id: 'SK', - name: 'Slovakia', + value: 'SK', + label: 'Slovakia', }, { - id: 'SI', - name: 'Slovenia', + value: 'SI', + label: 'Slovenia', }, { - id: 'ES', - name: 'Spain', + value: 'ES', + label: 'Spain', }, { - id: 'SE', - name: 'Sweden', + value: 'SE', + label: 'Sweden', }, { - id: 'GB', - name: 'United Kingdom', + value: 'GB', + label: 'United Kingdom', }, ]; diff --git a/upcoming-release-notes/784.md b/upcoming-release-notes/784.md new file mode 100644 index 00000000..4446470b --- /dev/null +++ b/upcoming-release-notes/784.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Reafctor `Nordigen` and category Autocomplete to the new react-select component