diff --git a/packages/desktop-client/src/components/autocomplete/AccountAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/AccountAutocomplete.tsx index 778480a7dff82db286cef18c2cdcdeceb399e3c8..4610f141eb44a6f315cd91101712f9d068bc11fe 100644 --- a/packages/desktop-client/src/components/autocomplete/AccountAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/AccountAutocomplete.tsx @@ -11,6 +11,7 @@ import { type CSSProperties, theme } from '../../style'; import { View } from '../common/View'; import { Autocomplete } from './Autocomplete'; +import { ItemHeader, type ItemHeaderProps } from './ItemHeader'; function AccountList({ items, @@ -71,9 +72,7 @@ function AccountList({ type AccountAutoCompleteProps = { embedded?: boolean; includeClosedAccounts: boolean; - renderAccountItemGroupHeader?: ( - props: AccountItemGroupHeaderProps, - ) => ReactNode; + renderAccountItemGroupHeader?: (props: ItemHeaderProps) => ReactNode; renderAccountItem?: (props: AccountItemProps) => ReactNode; closeOnBlur?: boolean; } & ComponentProps<typeof Autocomplete>; @@ -124,35 +123,10 @@ export function AccountAutocomplete({ ); } -type AccountItemGroupHeaderProps = { - title: string; - style?: CSSProperties; -}; - -export function AccountItemGroupHeader({ - title, - style, - ...props -}: AccountItemGroupHeaderProps) { - return ( - <div - style={{ - color: theme.menuAutoCompleteTextHeader, - padding: '4px 9px', - ...style, - }} - data-testid={`${title}-account-item-group`} - {...props} - > - {title} - </div> - ); -} - function defaultRenderAccountItemGroupHeader( - props: AccountItemGroupHeaderProps, + props: ItemHeaderProps, ): ReactNode { - return <AccountItemGroupHeader {...props} />; + return <ItemHeader {...props} type="account" />; } type AccountItemProps = { diff --git a/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx index bb34a8eeaa4e9ae180b27aa87d8235495419b6de..3716847b66194631a7a73089792eb087f2682674 100644 --- a/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx @@ -21,6 +21,7 @@ import { Text } from '../common/Text'; import { View } from '../common/View'; import { Autocomplete, defaultFilterSuggestion } from './Autocomplete'; +import { ItemHeader, type ItemHeaderProps } from './ItemHeader'; export type CategoryListProps = { items: Array<CategoryEntity & { group?: CategoryGroupEntity }>; @@ -33,9 +34,7 @@ export type CategoryListProps = { renderSplitTransactionButton?: ( props: SplitTransactionButtonProps, ) => ReactNode; - renderCategoryItemGroupHeader?: ( - props: CategoryItemGroupHeaderProps, - ) => ReactNode; + renderCategoryItemGroupHeader?: (props: ItemHeaderProps) => ReactNode; renderCategoryItem?: (props: CategoryItemProps) => ReactNode; }; function CategoryList({ @@ -103,9 +102,7 @@ type CategoryAutocompleteProps = ComponentProps<typeof Autocomplete> & { renderSplitTransactionButton?: ( props: SplitTransactionButtonProps, ) => ReactNode; - renderCategoryItemGroupHeader?: ( - props: CategoryItemGroupHeaderProps, - ) => ReactNode; + renderCategoryItemGroupHeader?: (props: ItemHeaderProps) => ReactNode; renderCategoryItem?: (props: CategoryItemProps) => ReactNode; }; @@ -177,35 +174,8 @@ export function CategoryAutocomplete({ ); } -type CategoryItemGroupHeaderProps = { - title: string; - style?: CSSProperties; -}; - -export function CategoryItemGroupHeader({ - title, - style, - ...props -}: CategoryItemGroupHeaderProps) { - return ( - <div - style={{ - color: theme.menuAutoCompleteTextHeader, - padding: '4px 9px', - ...style, - }} - data-testid={`${title}-category-item-group`} - {...props} - > - {title} - </div> - ); -} - -function defaultRenderCategoryItemGroupHeader( - props: CategoryItemGroupHeaderProps, -) { - return <CategoryItemGroupHeader {...props} />; +function defaultRenderCategoryItemGroupHeader(props: ItemHeaderProps) { + return <ItemHeader {...props} type="category" />; } type SplitTransactionButtonProps = { diff --git a/packages/desktop-client/src/components/autocomplete/FilterAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/FilterAutocomplete.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aa51cac180bb3c2066fa1bd9ab6ceea635e1e07a --- /dev/null +++ b/packages/desktop-client/src/components/autocomplete/FilterAutocomplete.tsx @@ -0,0 +1,34 @@ +import React, { type ComponentProps } from 'react'; + +import { useFilters } from 'loot-core/src/client/data-hooks/filters'; +import { type TransactionFilterEntity } from 'loot-core/types/models/transaction-filter'; + +import { Autocomplete } from './Autocomplete'; +import { FilterList } from './FilterList'; + +export function FilterAutocomplete({ + embedded, + ...props +}: { + embedded?: boolean; +} & ComponentProps<typeof Autocomplete<TransactionFilterEntity>>) { + const filters = useFilters() || []; + + return ( + <Autocomplete + strict={true} + highlightFirst={true} + embedded={embedded} + suggestions={filters} + renderItems={(items, getItemProps, highlightedIndex) => ( + <FilterList + items={items} + getItemProps={getItemProps} + highlightedIndex={highlightedIndex} + embedded={embedded} + /> + )} + {...props} + /> + ); +} diff --git a/packages/desktop-client/src/components/autocomplete/SavedFilterAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/FilterList.tsx similarity index 53% rename from packages/desktop-client/src/components/autocomplete/SavedFilterAutocomplete.tsx rename to packages/desktop-client/src/components/autocomplete/FilterList.tsx index a82ab2b22011fb6a24f95f72c8020ea551d0a6d6..7a8350b27fad3992757d89cd5b4f2c9f1e75d303 100644 --- a/packages/desktop-client/src/components/autocomplete/SavedFilterAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/FilterList.tsx @@ -1,26 +1,21 @@ import React, { type ComponentProps } from 'react'; -import { useFilters } from 'loot-core/src/client/data-hooks/filters'; -import { type TransactionFilterEntity } from 'loot-core/src/types/models'; - -import { theme } from '../../style'; +import { theme } from '../../style/theme'; import { View } from '../common/View'; -import { Autocomplete } from './Autocomplete'; +import { ItemHeader } from './ItemHeader'; -type FilterListProps<T> = { - items: T[]; - getItemProps: (arg: { item: T }) => ComponentProps<typeof View>; - highlightedIndex: number; - embedded?: boolean; -}; - -function FilterList<T extends { id: string; name: string }>({ +export function FilterList<T extends { id: string; name: string }>({ items, getItemProps, highlightedIndex, embedded, -}: FilterListProps<T>) { +}: { + items: T[]; + getItemProps: (arg: { item: T }) => ComponentProps<typeof View>; + highlightedIndex: number; + embedded?: boolean; +}) { return ( <View> <View @@ -30,6 +25,7 @@ function FilterList<T extends { id: string; name: string }>({ ...(!embedded && { maxHeight: 175 }), }} > + <ItemHeader title="Saved Filters" type="filter" /> {items.map((item, idx) => { return [ <div @@ -55,32 +51,3 @@ function FilterList<T extends { id: string; name: string }>({ </View> ); } - -type SavedFilterAutocompleteProps = { - embedded?: boolean; -} & ComponentProps<typeof Autocomplete<TransactionFilterEntity>>; - -export function SavedFilterAutocomplete({ - embedded, - ...props -}: SavedFilterAutocompleteProps) { - const filters = useFilters() || []; - - return ( - <Autocomplete - strict={true} - highlightFirst={true} - embedded={embedded} - suggestions={filters} - renderItems={(items, getItemProps, highlightedIndex) => ( - <FilterList - items={items} - getItemProps={getItemProps} - highlightedIndex={highlightedIndex} - embedded={embedded} - /> - )} - {...props} - /> - ); -} diff --git a/packages/desktop-client/src/components/autocomplete/ItemHeader.tsx b/packages/desktop-client/src/components/autocomplete/ItemHeader.tsx new file mode 100644 index 0000000000000000000000000000000000000000..981c05bec2695691a85067d0ba515d26ca775326 --- /dev/null +++ b/packages/desktop-client/src/components/autocomplete/ItemHeader.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import { theme } from '../../style/theme'; +import { type CSSProperties } from '../../style/types'; + +export type ItemHeaderProps = { + title: string; + style?: CSSProperties; + type?: string; +}; + +export function ItemHeader({ title, style, type, ...props }: ItemHeaderProps) { + return ( + <div + style={{ + color: theme.menuAutoCompleteTextHeader, + padding: '4px 9px', + ...style, + }} + data-testid={`${title}-${type}-item-group`} + {...props} + > + {title} + </div> + ); +} diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx index 84ee34bfd79d3967db97247d4037d7ef4fa3c7ad..5ab2b41ef051202604be38ba10aa76ae54452d7e 100644 --- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx +++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx @@ -32,6 +32,7 @@ import { defaultFilterSuggestion, AutocompleteFooter, } from './Autocomplete'; +import { ItemHeader, type ItemHeaderProps } from './ItemHeader'; function getPayeeSuggestions(payees, focusTransferPayees, accounts) { let activePayees = accounts ? getActivePayees(payees, accounts) : payees; @@ -163,7 +164,7 @@ type PayeeAutocompleteProps = { onSelect?: (value: string) => void; onManagePayees: () => void; renderCreatePayeeButton?: (props: CreatePayeeButtonProps) => ReactNode; - renderPayeeItemGroupHeader?: (props: PayeeItemGroupHeaderProps) => ReactNode; + renderPayeeItemGroupHeader?: (props: ItemHeaderProps) => ReactNode; renderPayeeItem?: (props: PayeeItemProps) => ReactNode; accounts?: AccountEntity[]; payees?: PayeeEntity[]; @@ -422,35 +423,8 @@ function defaultRenderCreatePayeeButton( return <CreatePayeeButton {...props} />; } -type PayeeItemGroupHeaderProps = { - title: string; - style?: CSSProperties; -}; - -export function PayeeItemGroupHeader({ - title, - style, - ...props -}: PayeeItemGroupHeaderProps) { - return ( - <div - style={{ - color: theme.menuAutoCompleteTextHeader, - padding: '4px 9px', - ...style, - }} - data-testid={`${title}-payee-item-group`} - {...props} - > - {title} - </div> - ); -} - -function defaultRenderPayeeItemGroupHeader( - props: PayeeItemGroupHeaderProps, -): ReactNode { - return <PayeeItemGroupHeader {...props} />; +function defaultRenderPayeeItemGroupHeader(props: ItemHeaderProps): ReactNode { + return <ItemHeader {...props} type="payee" />; } type PayeeItemProps = { diff --git a/packages/desktop-client/src/components/modals/EditField.jsx b/packages/desktop-client/src/components/modals/EditField.jsx index 0e2d3f1ff331a792a7c3c171b804e3c316a739a7..5b2b4d70b5af76da6dc40a48d480a35e498fb742 100644 --- a/packages/desktop-client/src/components/modals/EditField.jsx +++ b/packages/desktop-client/src/components/modals/EditField.jsx @@ -15,18 +15,16 @@ import { useResponsive } from '../../ResponsiveProvider'; import { styles, theme } from '../../style'; import { AccountAutocomplete, - AccountItemGroupHeader, AccountItem, } from '../autocomplete/AccountAutocomplete'; import { CategoryAutocomplete, - CategoryItemGroupHeader, CategoryItem, } from '../autocomplete/CategoryAutocomplete'; +import { ItemHeader } from '../autocomplete/ItemHeader'; import { PayeeAutocomplete, CreatePayeeButton, - PayeeItemGroupHeader, PayeeItem, } from '../autocomplete/PayeeAutocomplete'; import { Input } from '../common/Input'; @@ -118,7 +116,7 @@ export function EditField({ modalProps, name, onSubmit, onClose }) { }} {...(isNarrowWidth && { renderAccountItemGroupHeader: props => ( - <AccountItemGroupHeader + <ItemHeader {...props} style={{ ...styles.largeText, @@ -174,7 +172,7 @@ export function EditField({ modalProps, name, onSubmit, onClose }) { /> ), renderPayeeItemGroupHeader: props => ( - <PayeeItemGroupHeader + <ItemHeader {...props} style={{ ...styles.largeText, @@ -228,7 +226,7 @@ export function EditField({ modalProps, name, onSubmit, onClose }) { }} {...(isNarrowWidth && { renderCategoryItemGroupHeader: props => ( - <CategoryItemGroupHeader + <ItemHeader {...props} style={{ ...styles.largeText, diff --git a/packages/desktop-client/src/components/util/GenericInput.jsx b/packages/desktop-client/src/components/util/GenericInput.jsx index bf2e3c53d302eef47b1775b122c3854d01915a01..c2e1ec23a393b563b9ea583aaacda20f7b48c038 100644 --- a/packages/desktop-client/src/components/util/GenericInput.jsx +++ b/packages/desktop-client/src/components/util/GenericInput.jsx @@ -8,8 +8,8 @@ import { useDateFormat } from '../../hooks/useDateFormat'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { Autocomplete } from '../autocomplete/Autocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; +import { FilterAutocomplete } from '../autocomplete/FilterAutocomplete'; import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete'; -import { SavedFilterAutocomplete } from '../autocomplete/SavedFilterAutocomplete'; import { Input } from '../common/Input'; import { View } from '../common/View'; import { Checkbox } from '../forms'; @@ -98,7 +98,7 @@ export function GenericInput({ switch (field) { case 'saved': content = ( - <SavedFilterAutocomplete + <FilterAutocomplete saved={saved} value={value} multi={multi} diff --git a/upcoming-release-notes/2349.md b/upcoming-release-notes/2349.md new file mode 100644 index 0000000000000000000000000000000000000000..14be36df5010263f006d4fede62cd789b1e160f2 --- /dev/null +++ b/upcoming-release-notes/2349.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [carkom] +--- + +Organizing and splitting filters Autocomplete. Splitting out headers function that was duplicated in all autocomplete elements.