From 77b797813f29281e09aab541116dcf410b64dea2 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Wed, 17 Jul 2024 22:03:52 -0400 Subject: [PATCH 1/8] Correct table usage of `onBlur` --- packages/desktop-client/src/components/common/Input.tsx | 5 ++++- packages/desktop-client/src/components/table.tsx | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/desktop-client/src/components/common/Input.tsx b/packages/desktop-client/src/components/common/Input.tsx index 317c23a7616..7724faf7a2d 100644 --- a/packages/desktop-client/src/components/common/Input.tsx +++ b/packages/desktop-client/src/components/common/Input.tsx @@ -1,6 +1,8 @@ -import React, { +import type React from 'react'; +import { type InputHTMLAttributes, type KeyboardEvent, + type FocusEvent, type Ref, useRef, } from 'react'; @@ -28,6 +30,7 @@ type InputProps = InputHTMLAttributes & { onEscape?: (event: KeyboardEvent) => void; onChangeValue?: (newValue: string) => void; onUpdate?: (newValue: string) => void; + onBlur?: (event?: FocusEvent) => void; focused?: boolean; }; diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 7a7002410bd..77a9484b108 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -311,7 +311,7 @@ const readonlyInputStyle = { '::selection': { backgroundColor: theme.formInputTextReadOnlySelection }, }; -type InputValueProps = ComponentProps & { +type InputValueProps = Omit, 'value'> & { value?: string; }; @@ -705,10 +705,10 @@ export function SheetCell({ privacyFilter, } = valueProps; - const sheetValue = useSheetValue(binding, e => { + const sheetValue = useSheetValue(binding, () => { // "close" the cell if it's editing if (props.exposed && inputProps && inputProps.onBlur) { - inputProps.onBlur(e); + inputProps.onBlur(); } }); const format = useFormat(); From a9194b0ad0334cc94271d719c29b63b9e2104e1d Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Wed, 17 Jul 2024 22:05:11 -0400 Subject: [PATCH 2/8] Add basic spreadsheet typing structure --- .../src/components/spreadsheet/index.ts | 16 +++++++- .../desktop-client/src/components/table.tsx | 37 ++++++++++++++----- packages/loot-core/src/client/queries.ts | 21 +++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/packages/desktop-client/src/components/spreadsheet/index.ts b/packages/desktop-client/src/components/spreadsheet/index.ts index 0a069c8c65f..65d3368f2c6 100644 --- a/packages/desktop-client/src/components/spreadsheet/index.ts +++ b/packages/desktop-client/src/components/spreadsheet/index.ts @@ -1,4 +1,18 @@ // @ts-strict-ignore +import { + type SheetNames, + type BudgetField, + type SpreadsheetFieldTypes, +} from 'loot-core/client/queries'; import { type Query } from 'loot-core/src/shared/query'; -export type Binding = string | { name: string; value?; query?: Query }; +export type Binding< + SheetName extends SheetNames = any, + FieldName extends BudgetField = any, +> = + | FieldName + | { + name: FieldName; + value?: SpreadsheetFieldTypes[SheetName][FieldName]; + query?: Query; + }; diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 77a9484b108..601d6931bad 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -19,6 +19,12 @@ import React, { import { useStore } from 'react-redux'; import AutoSizer from 'react-virtualized-auto-sizer'; +import { + type SpreadsheetFieldTypes, + type BudgetField, + type SheetNames, +} from 'loot-core/client/queries'; + import { AvoidRefocusScrollProvider, useProperFocus, @@ -671,31 +677,42 @@ export function SelectCell({ ); } -type SheetCellValueProps = { - binding: Binding; +type SheetCellValueProps< + SheetName extends SheetNames, + FieldName extends BudgetField, +> = { + binding: Binding; type: FormatType; - getValueStyle?: (value: string | number) => CSSProperties; - formatExpr?: (value) => string; + getValueStyle?: ( + value: SpreadsheetFieldTypes[SheetName][FieldName], + ) => CSSProperties; + formatExpr?: (value: SpreadsheetFieldTypes[SheetName][FieldName]) => string; unformatExpr?: (value: string) => unknown; privacyFilter?: ComponentProps< typeof ConditionalPrivacyFilter >['privacyFilter']; }; -type SheetCellProps = ComponentProps & { - valueProps: SheetCellValueProps; +export type SheetCellProps< + SheetName extends SheetNames, + FieldName extends BudgetField, +> = ComponentProps & { + valueProps: SheetCellValueProps; inputProps?: Omit, 'value' | 'onUpdate'>; onSave?: (value) => void; textAlign?: CSSProperties['textAlign']; }; -export function SheetCell({ +export function SheetCell< + SheetName extends SheetNames = any, + FieldName extends BudgetField = any, +>({ valueProps, valueStyle, inputProps, textAlign, onSave, ...props -}: SheetCellProps) { +}: SheetCellProps) { const { binding, type, @@ -722,7 +739,7 @@ export function SheetCell({ } textAlign={textAlign} {...props} - value={sheetValue} + value={(sheetValue ?? '').toString()} formatter={value => props.formatter ? props.formatter(value, type) : format(value, type) } @@ -738,7 +755,7 @@ export function SheetCell({ {() => { return ( { onSave(unformatExpr ? unformatExpr(value) : value); }} diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts index d4320ee52be..2cae7cdfe33 100644 --- a/packages/loot-core/src/client/queries.ts +++ b/packages/loot-core/src/client/queries.ts @@ -11,6 +11,27 @@ import { import { q } from '../shared/query'; import { currencyToAmount, amountToInteger } from '../shared/util'; +export type SpreadsheetFieldTypes = { + account: { + // Common fields + 'uncategorized-amount': number; + 'uncategorized-balance': number; + + // Account fields + balance: number; + 'accounts-balance': number; + 'budgeted-accounts-balance': number; + 'offbudget-accounts-balance': number; + balanceCleared: number; + balanceUncleared: number; + }; +}; + +export type SheetNames = keyof SpreadsheetFieldTypes & string; + +export type BudgetField = + keyof SpreadsheetFieldTypes[SheetName] & string; + export function getAccountFilter(accountId, field = 'account') { if (accountId) { if (accountId === 'budgeted') { From 4d5e5a2e29e102772ff77a904a0295f07f7a6832 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Thu, 18 Jul 2024 01:30:52 -0400 Subject: [PATCH 3/8] Move to different module --- .../src/components/spreadsheet/index.ts | 29 ++++++++++++++----- .../desktop-client/src/components/table.tsx | 19 ++++++------ packages/loot-core/src/client/queries.ts | 21 -------------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/packages/desktop-client/src/components/spreadsheet/index.ts b/packages/desktop-client/src/components/spreadsheet/index.ts index 65d3368f2c6..768a1f26657 100644 --- a/packages/desktop-client/src/components/spreadsheet/index.ts +++ b/packages/desktop-client/src/components/spreadsheet/index.ts @@ -1,14 +1,29 @@ -// @ts-strict-ignore -import { - type SheetNames, - type BudgetField, - type SpreadsheetFieldTypes, -} from 'loot-core/client/queries'; import { type Query } from 'loot-core/src/shared/query'; +export type SpreadsheetFieldTypes = { + account: { + // Common fields + 'uncategorized-amount': number; + 'uncategorized-balance': number; + + // Account fields + balance: number; + 'accounts-balance': number; + 'budgeted-accounts-balance': number; + 'offbudget-accounts-balance': number; + balanceCleared: number; + balanceUncleared: number; + }; +}; + +export type SheetNames = keyof SpreadsheetFieldTypes & string; + +export type SheetFields = + keyof SpreadsheetFieldTypes[SheetName] & string; + export type Binding< SheetName extends SheetNames = any, - FieldName extends BudgetField = any, + FieldName extends SheetFields = any, > = | FieldName | { diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 601d6931bad..8438e480465 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -19,12 +19,6 @@ import React, { import { useStore } from 'react-redux'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { - type SpreadsheetFieldTypes, - type BudgetField, - type SheetNames, -} from 'loot-core/client/queries'; - import { AvoidRefocusScrollProvider, useProperFocus, @@ -46,7 +40,12 @@ import { ConditionalPrivacyFilter, mergeConditionalPrivacyFilterProps, } from './PrivacyFilter'; -import { type Binding } from './spreadsheet'; +import { + type SpreadsheetFieldTypes, + type SheetFields, + type SheetNames, + type Binding, +} from './spreadsheet'; import { type FormatType, useFormat } from './spreadsheet/useFormat'; import { useSheetValue } from './spreadsheet/useSheetValue'; @@ -679,7 +678,7 @@ export function SelectCell({ type SheetCellValueProps< SheetName extends SheetNames, - FieldName extends BudgetField, + FieldName extends SheetFields, > = { binding: Binding; type: FormatType; @@ -695,7 +694,7 @@ type SheetCellValueProps< export type SheetCellProps< SheetName extends SheetNames, - FieldName extends BudgetField, + FieldName extends SheetFields, > = ComponentProps & { valueProps: SheetCellValueProps; inputProps?: Omit, 'value' | 'onUpdate'>; @@ -704,7 +703,7 @@ export type SheetCellProps< }; export function SheetCell< SheetName extends SheetNames = any, - FieldName extends BudgetField = any, + FieldName extends SheetFields = any, >({ valueProps, valueStyle, diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts index 2cae7cdfe33..d4320ee52be 100644 --- a/packages/loot-core/src/client/queries.ts +++ b/packages/loot-core/src/client/queries.ts @@ -11,27 +11,6 @@ import { import { q } from '../shared/query'; import { currencyToAmount, amountToInteger } from '../shared/util'; -export type SpreadsheetFieldTypes = { - account: { - // Common fields - 'uncategorized-amount': number; - 'uncategorized-balance': number; - - // Account fields - balance: number; - 'accounts-balance': number; - 'budgeted-accounts-balance': number; - 'offbudget-accounts-balance': number; - balanceCleared: number; - balanceUncleared: number; - }; -}; - -export type SheetNames = keyof SpreadsheetFieldTypes & string; - -export type BudgetField = - keyof SpreadsheetFieldTypes[SheetName] & string; - export function getAccountFilter(accountId, field = 'account') { if (accountId) { if (accountId === 'budgeted') { From f407b5d16270bf6035b13ebe53f0daf63d5910c3 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Wed, 17 Jul 2024 22:05:11 -0400 Subject: [PATCH 4/8] Add account typing --- .../src/components/sidebar/Account.tsx | 10 ++-- packages/loot-core/src/client/queries.ts | 46 ++++++++++++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/desktop-client/src/components/sidebar/Account.tsx b/packages/desktop-client/src/components/sidebar/Account.tsx index b882d1cf8b2..28c4420a8aa 100644 --- a/packages/desktop-client/src/components/sidebar/Account.tsx +++ b/packages/desktop-client/src/components/sidebar/Account.tsx @@ -21,7 +21,7 @@ import { type OnDragChangeCallback, type OnDropCallback, } from '../sort'; -import { type Binding } from '../spreadsheet'; +import { type SheetFields, type Binding } from '../spreadsheet'; import { CellValue } from '../spreadsheet/CellValue'; export const accountNameStyle: CSSProperties = { @@ -37,10 +37,10 @@ export const accountNameStyle: CSSProperties = { ...styles.smallText, }; -type AccountProps = { +type AccountProps> = { name: string; to: string; - query: Binding; + query: Binding<'account', FieldName>; account?: AccountEntity; connected?: boolean; pending?: boolean; @@ -52,7 +52,7 @@ type AccountProps = { onDrop?: OnDropCallback; }; -export function Account({ +export function Account>({ name, account, connected, @@ -65,7 +65,7 @@ export function Account({ outerStyle, onDragChange, onDrop, -}: AccountProps) { +}: AccountProps) { const type = account ? account.closed ? 'account-closed' diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts index d4320ee52be..b7fa3e2d080 100644 --- a/packages/loot-core/src/client/queries.ts +++ b/packages/loot-core/src/client/queries.ts @@ -1,6 +1,10 @@ -// @ts-strict-ignore import { parse as parseDate, isValid as isDateValid } from 'date-fns'; +import { + type SheetNames, + type Binding, + type SheetFields, +} from '../../../desktop-client/src/components/spreadsheet'; import { dayFromDate, getDayMonthRegex, @@ -11,6 +15,13 @@ import { import { q } from '../shared/query'; import { currencyToAmount, amountToInteger } from '../shared/util'; +const parametrizedField = + () => + >(field: FieldName) => + (id: string): FieldName => + `${field}-${id}` as FieldName; +const accountParametrizedField = parametrizedField<'account'>(); + export function getAccountFilter(accountId, field = 'account') { if (accountId) { if (accountId === 'budgeted') { @@ -94,9 +105,9 @@ export function makeTransactionSearchQuery(currentQuery, search, dateFormat) { }); } -export function accountBalance(acct) { +export function accountBalance(acct): Binding<'account', 'balance'> { return { - name: `balance-${acct.id}`, + name: accountParametrizedField('balance')(acct.id), query: q('transactions') .filter({ account: acct.id }) .options({ splits: 'none' }) @@ -104,9 +115,11 @@ export function accountBalance(acct) { }; } -export function accountBalanceCleared(acct) { +export function accountBalanceCleared( + acct, +): Binding<'account', 'balanceCleared'> { return { - name: `balanceCleared-${acct.id}`, + name: accountParametrizedField('balanceCleared')(acct.id), query: q('transactions') .filter({ account: acct.id, cleared: true }) .options({ splits: 'none' }) @@ -114,9 +127,11 @@ export function accountBalanceCleared(acct) { }; } -export function accountBalanceUncleared(acct) { +export function accountBalanceUncleared( + acct, +): Binding<'account', 'balanceUncleared'> { return { - name: `balanceUncleared-${acct.id}`, + name: accountParametrizedField('balanceUncleared')(acct.id), query: q('transactions') .filter({ account: acct.id, cleared: false }) .options({ splits: 'none' }) @@ -124,7 +139,7 @@ export function accountBalanceUncleared(acct) { }; } -export function allAccountBalance() { +export function allAccountBalance(): Binding<'account', 'accounts-balance'> { return { query: q('transactions') .filter({ 'account.closed': false }) @@ -133,7 +148,10 @@ export function allAccountBalance() { }; } -export function budgetedAccountBalance() { +export function budgetedAccountBalance(): Binding< + 'account', + 'budgeted-accounts-balance' +> { return { name: `budgeted-accounts-balance`, query: q('transactions') @@ -142,7 +160,10 @@ export function budgetedAccountBalance() { }; } -export function offbudgetAccountBalance() { +export function offbudgetAccountBalance(): Binding< + 'account', + 'offbudget-accounts-balance' +> { return { name: `offbudget-accounts-balance`, query: q('transactions') @@ -210,7 +231,10 @@ export function uncategorizedBalance() { }; } -export function uncategorizedCount() { +export function uncategorizedCount(): Binding< + SheetName, + 'uncategorized-amount' +> { return { name: 'uncategorized-amount', query: uncategorizedQuery.calculate({ $count: '$id' }), From 3f2c46734eb84dc438e2cd30d5afc378e1085f97 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Thu, 18 Jul 2024 02:27:30 -0400 Subject: [PATCH 5/8] Add release notes --- upcoming-release-notes/3093.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 upcoming-release-notes/3093.md diff --git a/upcoming-release-notes/3093.md b/upcoming-release-notes/3093.md new file mode 100644 index 00000000000..10a21f26ecc --- /dev/null +++ b/upcoming-release-notes/3093.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [jfdoming] +--- + +Support type-checking on spreadsheet fields (part 1) From 4831db2661996fcf34ba3f09998c380976106b85 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Thu, 18 Jul 2024 02:29:06 -0400 Subject: [PATCH 6/8] Fix lint --- .../src/components/common/Input.tsx | 2 -- .../src/components/spreadsheet/index.ts | 2 ++ .../desktop-client/src/components/table.tsx | 11 ++++-- packages/loot-core/src/client/queries.ts | 35 +++++++++++++------ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/desktop-client/src/components/common/Input.tsx b/packages/desktop-client/src/components/common/Input.tsx index 7724faf7a2d..aeb41b4343e 100644 --- a/packages/desktop-client/src/components/common/Input.tsx +++ b/packages/desktop-client/src/components/common/Input.tsx @@ -2,7 +2,6 @@ import type React from 'react'; import { type InputHTMLAttributes, type KeyboardEvent, - type FocusEvent, type Ref, useRef, } from 'react'; @@ -30,7 +29,6 @@ type InputProps = InputHTMLAttributes & { onEscape?: (event: KeyboardEvent) => void; onChangeValue?: (newValue: string) => void; onUpdate?: (newValue: string) => void; - onBlur?: (event?: FocusEvent) => void; focused?: boolean; }; diff --git a/packages/desktop-client/src/components/spreadsheet/index.ts b/packages/desktop-client/src/components/spreadsheet/index.ts index 768a1f26657..7927ff45aad 100644 --- a/packages/desktop-client/src/components/spreadsheet/index.ts +++ b/packages/desktop-client/src/components/spreadsheet/index.ts @@ -22,7 +22,9 @@ export type SheetFields = keyof SpreadsheetFieldTypes[SheetName] & string; export type Binding< + // eslint-disable-next-line @typescript-eslint/no-explicit-any SheetName extends SheetNames = any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any FieldName extends SheetFields = any, > = | FieldName diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 8438e480465..3d36b679fa3 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -692,17 +692,24 @@ type SheetCellValueProps< >['privacyFilter']; }; -export type SheetCellProps< +type SheetCellProps< SheetName extends SheetNames, FieldName extends SheetFields, > = ComponentProps & { valueProps: SheetCellValueProps; - inputProps?: Omit, 'value' | 'onUpdate'>; + inputProps?: Omit< + ComponentProps, + 'value' | 'onUpdate' | 'onBlur' + > & { + onBlur?: () => void; + }; onSave?: (value) => void; textAlign?: CSSProperties['textAlign']; }; export function SheetCell< + // eslint-disable-next-line @typescript-eslint/no-explicit-any SheetName extends SheetNames = any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any FieldName extends SheetFields = any, >({ valueProps, diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts index b7fa3e2d080..38952a276a6 100644 --- a/packages/loot-core/src/client/queries.ts +++ b/packages/loot-core/src/client/queries.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { parse as parseDate, isValid as isDateValid } from 'date-fns'; import { @@ -12,8 +13,10 @@ import { getShortYearRegex, getShortYearFormat, } from '../shared/months'; -import { q } from '../shared/query'; +import { q, type Query } from '../shared/query'; import { currencyToAmount, amountToInteger } from '../shared/util'; +import { type CategoryEntity, type AccountEntity } from '../types/models'; +import { type LocalPrefs } from '../types/prefs'; const parametrizedField = () => @@ -22,7 +25,7 @@ const parametrizedField = `${field}-${id}` as FieldName; const accountParametrizedField = parametrizedField<'account'>(); -export function getAccountFilter(accountId, field = 'account') { +export function getAccountFilter(accountId: string, field = 'account') { if (accountId) { if (accountId === 'budgeted') { return { @@ -58,7 +61,7 @@ export function getAccountFilter(accountId, field = 'account') { return null; } -export function makeTransactionsQuery(accountId) { +export function makeTransactionsQuery(accountId: string) { let query = q('transactions').options({ splits: 'grouped' }); const filter = getAccountFilter(accountId); @@ -69,7 +72,11 @@ export function makeTransactionsQuery(accountId) { return query; } -export function makeTransactionSearchQuery(currentQuery, search, dateFormat) { +export function makeTransactionSearchQuery( + currentQuery: Query, + search: string, + dateFormat: LocalPrefs['dateFormat'], +) { const amount = currencyToAmount(search); // Support various date formats @@ -105,7 +112,9 @@ export function makeTransactionSearchQuery(currentQuery, search, dateFormat) { }); } -export function accountBalance(acct): Binding<'account', 'balance'> { +export function accountBalance( + acct: AccountEntity, +): Binding<'account', 'balance'> { return { name: accountParametrizedField('balance')(acct.id), query: q('transactions') @@ -116,7 +125,7 @@ export function accountBalance(acct): Binding<'account', 'balance'> { } export function accountBalanceCleared( - acct, + acct: AccountEntity, ): Binding<'account', 'balanceCleared'> { return { name: accountParametrizedField('balanceCleared')(acct.id), @@ -128,7 +137,7 @@ export function accountBalanceCleared( } export function accountBalanceUncleared( - acct, + acct: AccountEntity, ): Binding<'account', 'balanceUncleared'> { return { name: accountParametrizedField('balanceUncleared')(acct.id), @@ -172,7 +181,7 @@ export function offbudgetAccountBalance(): Binding< }; } -export function categoryBalance(category, month) { +export function categoryBalance(category: CategoryEntity, month: string) { return { name: `balance-${category.id}`, query: q('transactions') @@ -185,7 +194,10 @@ export function categoryBalance(category, month) { }; } -export function categoryBalanceCleared(category, month) { +export function categoryBalanceCleared( + category: CategoryEntity, + month: string, +) { return { name: `balanceCleared-${category.id}`, query: q('transactions') @@ -199,7 +211,10 @@ export function categoryBalanceCleared(category, month) { }; } -export function categoryBalanceUncleared(category, month) { +export function categoryBalanceUncleared( + category: CategoryEntity, + month: string, +) { return { name: `balanceUncleared-${category.id}`, query: q('transactions') From 6d8b6f3cc70c6372a537364d8659452ec44ef819 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Thu, 18 Jul 2024 03:32:57 -0400 Subject: [PATCH 7/8] Remove unneeded diff --- packages/desktop-client/src/components/common/Input.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/desktop-client/src/components/common/Input.tsx b/packages/desktop-client/src/components/common/Input.tsx index aeb41b4343e..317c23a7616 100644 --- a/packages/desktop-client/src/components/common/Input.tsx +++ b/packages/desktop-client/src/components/common/Input.tsx @@ -1,5 +1,4 @@ -import type React from 'react'; -import { +import React, { type InputHTMLAttributes, type KeyboardEvent, type Ref, From 8aad0d667b20ab7085716066e5e99e3d4221e680 Mon Sep 17 00:00:00 2001 From: Julian Dominguez-Schatz Date: Sat, 27 Jul 2024 15:28:06 -0400 Subject: [PATCH 8/8] PR feedback --- .../src/components/spreadsheet/index.ts | 19 ++++++++++++------- .../desktop-client/src/components/table.tsx | 10 ++++------ packages/loot-core/src/client/queries.ts | 9 ++------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/desktop-client/src/components/spreadsheet/index.ts b/packages/desktop-client/src/components/spreadsheet/index.ts index 7927ff45aad..5e3df0052be 100644 --- a/packages/desktop-client/src/components/spreadsheet/index.ts +++ b/packages/desktop-client/src/components/spreadsheet/index.ts @@ -1,6 +1,6 @@ import { type Query } from 'loot-core/src/shared/query'; -export type SpreadsheetFieldTypes = { +export type Spreadsheets = { account: { // Common fields 'uncategorized-amount': number; @@ -16,20 +16,25 @@ export type SpreadsheetFieldTypes = { }; }; -export type SheetNames = keyof SpreadsheetFieldTypes & string; +export type SheetNames = keyof Spreadsheets & string; export type SheetFields = - keyof SpreadsheetFieldTypes[SheetName] & string; + keyof Spreadsheets[SheetName] & string; export type Binding< // eslint-disable-next-line @typescript-eslint/no-explicit-any SheetName extends SheetNames = any, // eslint-disable-next-line @typescript-eslint/no-explicit-any - FieldName extends SheetFields = any, + SheetFieldName extends SheetFields = any, > = - | FieldName + | SheetFieldName | { - name: FieldName; - value?: SpreadsheetFieldTypes[SheetName][FieldName]; + name: SheetFieldName; + value?: Spreadsheets[SheetName][SheetFieldName]; query?: Query; }; +export const parametrizedField = + () => + >(field: SheetFieldName) => + (id: string): SheetFieldName => + `${field}-${id}` as SheetFieldName; diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx index 3d36b679fa3..0d3317d9a8d 100644 --- a/packages/desktop-client/src/components/table.tsx +++ b/packages/desktop-client/src/components/table.tsx @@ -41,7 +41,7 @@ import { mergeConditionalPrivacyFilterProps, } from './PrivacyFilter'; import { - type SpreadsheetFieldTypes, + type Spreadsheets, type SheetFields, type SheetNames, type Binding, @@ -682,10 +682,8 @@ type SheetCellValueProps< > = { binding: Binding; type: FormatType; - getValueStyle?: ( - value: SpreadsheetFieldTypes[SheetName][FieldName], - ) => CSSProperties; - formatExpr?: (value: SpreadsheetFieldTypes[SheetName][FieldName]) => string; + getValueStyle?: (value: Spreadsheets[SheetName][FieldName]) => CSSProperties; + formatExpr?: (value: Spreadsheets[SheetName][FieldName]) => string; unformatExpr?: (value: string) => unknown; privacyFilter?: ComponentProps< typeof ConditionalPrivacyFilter @@ -745,7 +743,7 @@ export function SheetCell< } textAlign={textAlign} {...props} - value={(sheetValue ?? '').toString()} + value={String(sheetValue ?? '')} formatter={value => props.formatter ? props.formatter(value, type) : format(value, type) } diff --git a/packages/loot-core/src/client/queries.ts b/packages/loot-core/src/client/queries.ts index 38952a276a6..2db11cc9591 100644 --- a/packages/loot-core/src/client/queries.ts +++ b/packages/loot-core/src/client/queries.ts @@ -2,9 +2,9 @@ import { parse as parseDate, isValid as isDateValid } from 'date-fns'; import { - type SheetNames, + parametrizedField, type Binding, - type SheetFields, + type SheetNames, } from '../../../desktop-client/src/components/spreadsheet'; import { dayFromDate, @@ -18,11 +18,6 @@ import { currencyToAmount, amountToInteger } from '../shared/util'; import { type CategoryEntity, type AccountEntity } from '../types/models'; import { type LocalPrefs } from '../types/prefs'; -const parametrizedField = - () => - >(field: FieldName) => - (id: string): FieldName => - `${field}-${id}` as FieldName; const accountParametrizedField = parametrizedField<'account'>(); export function getAccountFilter(accountId: string, field = 'account') {