diff --git a/packages/desktop-client/e2e/page-models/account-page.js b/packages/desktop-client/e2e/page-models/account-page.js index aa23d041867c2c68816d37b343ca4dab18793e43..01fb45dbac261c718b7b1a6b32ecef5e05d9e147 100644 --- a/packages/desktop-client/e2e/page-models/account-page.js +++ b/packages/desktop-client/e2e/page-models/account-page.js @@ -16,8 +16,8 @@ export class AccountPage { this.cancelTransactionButton = this.page.getByRole('button', { name: 'Cancel', }); - this.menuButton = this.page.getByRole('button', { - name: 'Menu', + this.accountMenuButton = this.page.getByRole('button', { + name: 'Account menu', }); this.transactionTable = this.page.getByTestId('transaction-table'); @@ -103,7 +103,7 @@ export class AccountPage { * Open the modal for closing the account. */ async clickCloseAccount() { - await this.menuButton.click(); + await this.accountMenuButton.click(); await this.page.getByRole('button', { name: 'Close Account' }).click(); return new CloseAccountModal( this.page.getByTestId('close-account-modal'), diff --git a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png index 4a395ced02da2ccc74fd6b885ed51f39bb2e9532..d4b7313b410e7149c074209be960ed992930b223 100644 Binary files a/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png and b/packages/desktop-client/e2e/rules.test.js-snapshots/Rules-creates-a-rule-and-makes-sure-it-is-applied-when-creating-a-transaction-4-chromium-linux.png differ diff --git a/packages/desktop-client/src/components/FatalError.tsx b/packages/desktop-client/src/components/FatalError.tsx index 58e2ec114bfdca98d66b8b81b6ada81acfa27942..724df4dbdf73e7dcd6de8a10ed6f31c6ea8ba81d 100644 --- a/packages/desktop-client/src/components/FatalError.tsx +++ b/packages/desktop-client/src/components/FatalError.tsx @@ -3,7 +3,7 @@ import React, { useState, type ReactNode } from 'react'; import { LazyLoadFailedError } from 'loot-core/src/shared/errors'; import { Block } from './common/Block'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Link } from './common/Link'; import { Modal } from './common/Modal'; import { Paragraph } from './common/Paragraph'; @@ -149,8 +149,8 @@ function SharedArrayBufferOverride() { I understand the risks, run Actual in the unsupported fallback mode </label> <Button - disabled={!understand} - onClick={() => { + isDisabled={!understand} + onPress={() => { window.localStorage.setItem('SharedArrayBufferOverride', 'true'); window.location.reload(); }} @@ -191,7 +191,7 @@ export function FatalError({ error }: FatalErrorProps) { )} <Paragraph> - <Button onClick={() => window.Actual?.relaunch()}>Restart app</Button> + <Button onPress={() => window.Actual?.relaunch()}>Restart app</Button> </Paragraph> <Paragraph isLast={true} style={{ fontSize: 11 }}> <Link variant="text" onClick={() => setShowError(state => !state)}> diff --git a/packages/desktop-client/src/components/LoggedInUser.tsx b/packages/desktop-client/src/components/LoggedInUser.tsx index c9eba0badb8517bd6f02ca2323debb1241aac2c3..0df0d616f082e7f692c75d45e0210e3e487c1b22 100644 --- a/packages/desktop-client/src/components/LoggedInUser.tsx +++ b/packages/desktop-client/src/components/LoggedInUser.tsx @@ -7,7 +7,7 @@ import { type State } from 'loot-core/src/client/state-types'; import { useActions } from '../hooks/useActions'; import { theme, styles, type CSSProperties } from '../style'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Menu } from './common/Menu'; import { Popover } from './common/Popover'; import { Text } from './common/Text'; @@ -97,8 +97,8 @@ export function LoggedInUser({ <View style={{ flexDirection: 'row', alignItems: 'center', ...style }}> <Button ref={triggerRef} - type="bare" - onClick={() => setMenuOpen(true)} + variant="bare" + onPress={() => setMenuOpen(true)} style={color && { color }} > {serverMessage()} diff --git a/packages/desktop-client/src/components/ManageRules.tsx b/packages/desktop-client/src/components/ManageRules.tsx index 79bc64ec414bc5e5eccae26804f0981429fde232..d627b98e367bf37bf65fc3145bda0a91655b6838 100644 --- a/packages/desktop-client/src/components/ManageRules.tsx +++ b/packages/desktop-client/src/components/ManageRules.tsx @@ -24,7 +24,7 @@ import { usePayees } from '../hooks/usePayees'; import { useSelected, SelectedProvider } from '../hooks/useSelected'; import { theme } from '../style'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Link } from './common/Link'; import { Search } from './common/Search'; import { Stack } from './common/Stack'; @@ -314,11 +314,11 @@ function ManageRulesContent({ > <Stack direction="row" align="center" justify="flex-end" spacing={2}> {selectedInst.items.size > 0 && ( - <Button onClick={onDeleteSelected}> + <Button onPress={onDeleteSelected}> Delete {selectedInst.items.size} rules </Button> )} - <Button type="primary" onClick={onCreateRule}> + <Button variant="primary" onPress={onCreateRule}> Create new rule </Button> </Stack> diff --git a/packages/desktop-client/src/components/NotesButton.tsx b/packages/desktop-client/src/components/NotesButton.tsx index 901442b8c953c74bd00697e8ae50bc9832f406d3..de5c928d7ac516641a7036ab236c315133cc3a07 100644 --- a/packages/desktop-client/src/components/NotesButton.tsx +++ b/packages/desktop-client/src/components/NotesButton.tsx @@ -6,7 +6,7 @@ import { useNotes } from '../hooks/useNotes'; import { SvgCustomNotesPaper } from '../icons/v2'; import { type CSSProperties, theme } from '../style'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Popover } from './common/Popover'; import { Tooltip } from './common/Tooltip'; import { View } from './common/View'; @@ -52,7 +52,7 @@ export function NotesButton({ <View style={{ flexShrink: 0 }}> <Button ref={triggerRef} - type="bare" + variant="bare" aria-label="View notes" className={!hasNotes && !isOpen ? 'hover-visible' : ''} style={{ @@ -61,8 +61,7 @@ export function NotesButton({ ...(hasNotes && { display: 'flex !important' }), ...(isOpen && { color: theme.buttonNormalText }), }} - onClick={event => { - event.stopPropagation(); + onPress={() => { setIsOpen(true); }} > diff --git a/packages/desktop-client/src/components/Notifications.tsx b/packages/desktop-client/src/components/Notifications.tsx index 1ed01bdce3f09e4e25c1ae937005fa64191c811c..185241524895bfd795fb55eb5d33201ff343ed9a 100644 --- a/packages/desktop-client/src/components/Notifications.tsx +++ b/packages/desktop-client/src/components/Notifications.tsx @@ -16,7 +16,7 @@ import { SvgDelete } from '../icons/v0'; import { useResponsive } from '../ResponsiveProvider'; import { styles, theme, type CSSProperties } from '../style'; -import { Button, ButtonWithLoading } from './common/Button'; +import { Button, ButtonWithLoading } from './common/Button2'; import { Link } from './common/Link'; import { Stack } from './common/Stack'; import { Text } from './common/Text'; @@ -178,15 +178,15 @@ function Notification({ : null} {button && ( <ButtonWithLoading - type="bare" - loading={loading} - onClick={async () => { + variant="bare" + isLoading={loading} + onPress={async () => { setLoading(true); await button.action(); onRemove(); setLoading(false); }} - style={{ + style={({ isHovered, isPressed }) => ({ backgroundColor: 'transparent', border: `1px solid ${ positive @@ -198,14 +198,16 @@ function Notification({ color: 'currentColor', fontSize: 14, flexShrink: 0, - '&:hover, &:active': { - backgroundColor: positive - ? theme.noticeBackground - : error - ? theme.errorBackground - : theme.warningBackground, - }, - }} + ...(isHovered || isPressed + ? { + backgroundColor: positive + ? theme.noticeBackground + : error + ? theme.errorBackground + : theme.warningBackground, + } + : {}), + })} > {button.title} </ButtonWithLoading> @@ -213,10 +215,10 @@ function Notification({ </Stack> {sticky && ( <Button - type="bare" + variant="bare" aria-label="Close" style={{ flexShrink: 0, color: 'currentColor' }} - onClick={onRemove} + onPress={onRemove} > <SvgDelete style={{ width: 9, height: 9, color: 'currentColor' }} /> </Button> diff --git a/packages/desktop-client/src/components/ThemeSelector.tsx b/packages/desktop-client/src/components/ThemeSelector.tsx index e07bcd13ac2dc07827f38809b0cabb72a18c6e9f..11ba1e817e8b6f3e7cfc8f7cd2cc8b16b3c48643 100644 --- a/packages/desktop-client/src/components/ThemeSelector.tsx +++ b/packages/desktop-client/src/components/ThemeSelector.tsx @@ -6,7 +6,7 @@ import { SvgMoonStars, SvgSun, SvgSystem } from '../icons/v2'; import { useResponsive } from '../ResponsiveProvider'; import { type CSSProperties, themeOptions, useTheme } from '../style'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Menu } from './common/Menu'; import { Popover } from './common/Popover'; @@ -44,9 +44,9 @@ export function ThemeSelector({ style }: ThemeSelectorProps) { <> <Button ref={triggerRef} - type="bare" + variant="bare" aria-label="Switch theme" - onClick={() => setMenuOpen(true)} + onPress={() => setMenuOpen(true)} style={style} > <Icon style={{ width: 13, height: 13, color: 'inherit' }} /> diff --git a/packages/desktop-client/src/components/Titlebar.tsx b/packages/desktop-client/src/components/Titlebar.tsx index 4336ef4e0f89e55113e852c8e1806c07493d80ad..9c25bbd10d0492d6a85dbc9f90f47866e954d457 100644 --- a/packages/desktop-client/src/components/Titlebar.tsx +++ b/packages/desktop-client/src/components/Titlebar.tsx @@ -24,7 +24,7 @@ import { theme, type CSSProperties, styles } from '../style'; import { AccountSyncCheck } from './accounts/AccountSyncCheck'; import { AnimatedRefresh } from './AnimatedRefresh'; import { MonthCountSelector } from './budget/MonthCountSelector'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Link } from './common/Link'; import { Text } from './common/Text'; import { View } from './common/View'; @@ -78,9 +78,9 @@ function PrivacyButton({ style }: PrivacyButtonProps) { return ( <Button - type="bare" + variant="bare" aria-label={`${isPrivacyEnabled ? 'Disable' : 'Enable'} privacy mode`} - onClick={() => setPrivacyEnabledPref(!isPrivacyEnabled)} + onPress={() => setPrivacyEnabledPref(!isPrivacyEnabled)} style={style} > {isPrivacyEnabled ? ( @@ -196,10 +196,10 @@ function SyncButton({ style, isMobile = false }: SyncButtonProps) { return ( <Button - type="bare" + variant="bare" aria-label="Sync" - style={ - isMobile + style={({ isHovered, isPressed }) => ({ + ...(isMobile ? { ...style, WebkitAppRegion: 'none', @@ -209,11 +209,11 @@ function SyncButton({ style, isMobile = false }: SyncButtonProps) { ...style, WebkitAppRegion: 'none', color: desktopColor, - } - } - hoveredStyle={hoveredStyle} - activeStyle={activeStyle} - onClick={sync} + }), + ...(isHovered ? hoveredStyle : {}), + ...(isPressed ? activeStyle : {}), + })} + onPress={sync} > {isMobile ? ( syncState === 'error' ? ( @@ -281,14 +281,15 @@ export function Titlebar({ style }: TitlebarProps) { > {(floatingSidebar || sidebar.alwaysFloats) && ( <Button - type="bare" + aria-label="Sidebar menu" + variant="bare" style={{ marginRight: 8 }} - onPointerEnter={e => { + onHoverStart={e => { if (e.pointerType === 'mouse') { sidebar.setHidden(false); } }} - onPointerUp={e => { + onPress={e => { if (e.pointerType !== 'mouse') { sidebar.setHidden(!sidebar.hidden); } @@ -306,7 +307,7 @@ export function Titlebar({ style }: TitlebarProps) { path="/accounts" element={ location.state?.goBack ? ( - <Button type="bare" onClick={() => navigate(-1)}> + <Button variant="bare" onPress={() => navigate(-1)}> <SvgArrowLeft width={10} height={10} diff --git a/packages/desktop-client/src/components/UpdateNotification.tsx b/packages/desktop-client/src/components/UpdateNotification.tsx index 2ce4831e24b633726a77c609e1148e519ca1797e..e2f9e60b7d62e36ef38167579c45daf368d5cf79 100644 --- a/packages/desktop-client/src/components/UpdateNotification.tsx +++ b/packages/desktop-client/src/components/UpdateNotification.tsx @@ -7,7 +7,7 @@ import { useActions } from '../hooks/useActions'; import { SvgClose } from '../icons/v1'; import { theme } from '../style'; -import { Button } from './common/Button'; +import { Button } from './common/Button2'; import { Link } from './common/Link'; import { Text } from './common/Text'; import { View } from './common/View'; @@ -72,10 +72,10 @@ export function UpdateNotification() { </Link> ) <Button - type="bare" + variant="bare" aria-label="Close" style={{ display: 'inline', padding: '1px 7px 2px 7px' }} - onClick={() => { + onPress={() => { // Set a flag to never show an update notification again for this session setAppState({ updateInfo: null, diff --git a/packages/desktop-client/src/components/accounts/Header.jsx b/packages/desktop-client/src/components/accounts/Header.jsx index d5591ccbf9a143e80cb40c659a4b778b0de4adfe..997f36298ac66388728bb14351fc0aad3a7e9570 100644 --- a/packages/desktop-client/src/components/accounts/Header.jsx +++ b/packages/desktop-client/src/components/accounts/Header.jsx @@ -379,7 +379,11 @@ export function AccountHeader({ </Button> {account ? ( <View> - <MenuButton ref={triggerRef} onClick={() => setMenuOpen(true)} /> + <MenuButton + aria-label="Account menu" + ref={triggerRef} + onPress={() => setMenuOpen(true)} + /> <Popover triggerRef={triggerRef} @@ -406,7 +410,11 @@ export function AccountHeader({ </View> ) : ( <View> - <MenuButton ref={triggerRef} onClick={() => setMenuOpen(true)} /> + <MenuButton + aria-label="Account menu" + ref={triggerRef} + onPress={() => setMenuOpen(true)} + /> <Popover triggerRef={triggerRef} diff --git a/packages/desktop-client/src/components/common/Button.tsx b/packages/desktop-client/src/components/common/Button.tsx index c7a423397585d460768cdb87b5e2f7eb85607819..0cb88a7a638fa5e7e26100143d438892360bfea3 100644 --- a/packages/desktop-client/src/components/common/Button.tsx +++ b/packages/desktop-client/src/components/common/Button.tsx @@ -198,43 +198,42 @@ type ButtonWithLoadingProps = ButtonProps & { loading?: boolean; }; -export const ButtonWithLoading = forwardRef< - HTMLButtonElement, - ButtonWithLoadingProps ->((props, ref) => { - const { loading, children, ...buttonProps } = props; - return ( - <Button - {...buttonProps} - ref={ref} - style={{ position: 'relative', ...buttonProps.style }} - > - {loading && ( +const ButtonWithLoading = forwardRef<HTMLButtonElement, ButtonWithLoadingProps>( + (props, ref) => { + const { loading, children, ...buttonProps } = props; + return ( + <Button + {...buttonProps} + ref={ref} + style={{ position: 'relative', ...buttonProps.style }} + > + {loading && ( + <View + style={{ + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + alignItems: 'center', + justifyContent: 'center', + }} + > + <AnimatedLoading style={{ width: 20, height: 20 }} /> + </View> + )} <View style={{ - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, + opacity: loading ? 0 : 1, + flexDirection: 'row', alignItems: 'center', - justifyContent: 'center', }} > - <AnimatedLoading style={{ width: 20, height: 20 }} /> + {children} </View> - )} - <View - style={{ - opacity: loading ? 0 : 1, - flexDirection: 'row', - alignItems: 'center', - }} - > - {children} - </View> - </Button> - ); -}); + </Button> + ); + }, +); ButtonWithLoading.displayName = 'ButtonWithLoading'; diff --git a/packages/desktop-client/src/components/common/Button2.tsx b/packages/desktop-client/src/components/common/Button2.tsx index 78d3d1735ea2c1dbff0cfd004f3ba9ae167e4962..a48c1bb8dc3ebfa31fbf575370428e60905f4edb 100644 --- a/packages/desktop-client/src/components/common/Button2.tsx +++ b/packages/desktop-client/src/components/common/Button2.tsx @@ -1,9 +1,11 @@ import React, { forwardRef, type ComponentPropsWithoutRef } from 'react'; import { + type ButtonRenderProps as ReactAriaButtonRenderProps, Button as ReactAriaButton, - type ButtonProps as ReactAriaButtonProps, } from 'react-aria-components'; +import { css } from 'glamor'; + import { AnimatedLoading } from '../../icons/AnimatedLoading'; import { type CSSProperties, styles, theme } from '../../style'; @@ -125,17 +127,10 @@ type ButtonVariant = 'normal' | 'primary' | 'bare' | 'menu' | 'menuSelected'; export const Button = forwardRef<HTMLButtonElement, ButtonProps>( (props, ref) => { - const { - children, - variant = 'normal', - bounce = true, - style, - isDisabled, - ...restProps - } = props; + const { children, variant = 'normal', bounce = true, ...restProps } = props; const variantWithDisabled: ButtonVariant | `${ButtonVariant}Disabled` = - isDisabled ? `${variant}Disabled` : variant; + props.isDisabled ? `${variant}Disabled` : variant; const hoveredStyle = { ...(variant !== 'bare' && styles.shadow), @@ -143,39 +138,44 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>( color: textColorHover[variant], cursor: 'pointer', }; - const pressedStyle = { + const activeStyle = { ..._getActiveStyles(variant, bounce), }; - const buttonStyle: ComponentPropsWithoutRef< - typeof Button - >['style'] = props => ({ - ...props.defaultStyle, - alignItems: 'center', - justifyContent: 'center', - flexShrink: 0, - padding: _getPadding(variant), - margin: 0, - overflow: 'hidden', - display: 'flex', - borderRadius: 4, - backgroundColor: backgroundColor[variantWithDisabled], - border: _getBorder(variant, variantWithDisabled), - color: textColor[variantWithDisabled], - transition: 'box-shadow .25s', - WebkitAppRegion: 'no-drag', - ...styles.smallText, - ...(props.isHovered && !isDisabled ? hoveredStyle : {}), - ...(props.isPressed && !isDisabled ? pressedStyle : {}), - ...(typeof style === 'function' ? style(props) : style), - }); + const defaultButtonClassName: ReactAriaButtonClassNameFn = renderProps => + String( + css({ + alignItems: 'center', + justifyContent: 'center', + flexShrink: 0, + padding: _getPadding(variant), + margin: 0, + overflow: 'hidden', + display: 'flex', + borderRadius: 4, + backgroundColor: backgroundColor[variantWithDisabled], + border: _getBorder(variant, variantWithDisabled), + color: textColor[variantWithDisabled], + transition: 'box-shadow .25s', + WebkitAppRegion: 'no-drag', + ...styles.smallText, + ...(renderProps.isDisabled ? {} : { ':hover': hoveredStyle }), + ...(renderProps.isDisabled ? {} : { ':active': activeStyle }), + }), + ); + + const buttonClassName: ReactAriaButtonClassNameFn = renderProps => + typeof props.className === 'function' + ? props.className(renderProps) + : props.className || ''; return ( <ReactAriaButton ref={ref} - isDisabled={isDisabled} - style={buttonStyle} {...restProps} + className={renderProps => + `${renderProps.defaultClassName} ${defaultButtonClassName(renderProps)} ${buttonClassName(renderProps)}` + } > {children} </ReactAriaButton> @@ -193,12 +193,15 @@ export const ButtonWithLoading = forwardRef< HTMLButtonElement, ButtonWithLoadingProps >((props, ref) => { - const { isLoading, children, ...buttonProps } = props; + const { isLoading, children, style, ...buttonProps } = props; return ( <Button {...buttonProps} ref={ref} - style={{ position: 'relative', ...buttonProps.style }} + style={buttonRenderProps => ({ + position: 'relative', + ...(typeof style === 'function' ? style(buttonRenderProps) : style), + })} > {renderProps => ( <> @@ -233,3 +236,10 @@ export const ButtonWithLoading = forwardRef< }); ButtonWithLoading.displayName = 'ButtonWithLoading'; + +type ReactAriaButtonClassNameFn = Extract< + ComponentPropsWithoutRef<typeof ReactAriaButton>['className'], + ( + renderProps: ReactAriaButtonRenderProps & { defaultClassName: string }, + ) => string +>; diff --git a/packages/desktop-client/src/components/common/MenuButton.tsx b/packages/desktop-client/src/components/common/MenuButton.tsx index 452c6b642cef6a9d3a9c57d8b0fc6e96391b3863..bf55a955cc52120a1772da4b77ac2b93421a919d 100644 --- a/packages/desktop-client/src/components/common/MenuButton.tsx +++ b/packages/desktop-client/src/components/common/MenuButton.tsx @@ -1,25 +1,15 @@ -import React, { forwardRef } from 'react'; +import React, { type ComponentPropsWithoutRef, forwardRef } from 'react'; import { SvgDotsHorizontalTriple } from '../../icons/v1'; -import { type CSSProperties } from '../../style'; -import { Button } from './Button'; +import { Button } from './Button2'; -type MenuButtonProps = { - onClick: () => void; - style?: CSSProperties; -}; +type MenuButtonProps = ComponentPropsWithoutRef<typeof Button>; export const MenuButton = forwardRef<HTMLButtonElement, MenuButtonProps>( - ({ onClick, style }, ref) => { + (props, ref) => { return ( - <Button - ref={ref} - type="bare" - onClick={onClick} - aria-label="Menu" - style={style} - > + <Button ref={ref} variant="bare" aria-label="Menu" {...props}> <SvgDotsHorizontalTriple width={15} height={15} diff --git a/packages/desktop-client/src/components/reports/reports/ListCardsPopover.tsx b/packages/desktop-client/src/components/reports/reports/ListCardsPopover.tsx index c0daf34b0edd64eceb1b2b1ce72ac86cae9676c7..7b2a2817ba240b28827eabf0571a42227157e600 100644 --- a/packages/desktop-client/src/components/reports/reports/ListCardsPopover.tsx +++ b/packages/desktop-client/src/components/reports/reports/ListCardsPopover.tsx @@ -47,8 +47,9 @@ export function ListCardsPopover({ return ( <> <MenuButton + aria-label="Report menu" ref={triggerRef} - onClick={() => + onPress={() => onMenuOpen(report.id === undefined ? '' : report.id, true) } style={{ diff --git a/packages/desktop-client/src/components/select/RecurringSchedulePicker.jsx b/packages/desktop-client/src/components/select/RecurringSchedulePicker.jsx index 27df5bf61666ee482a40b63a364640b29be80915..20244423f917711a611bb80f7bbe65d250927950 100644 --- a/packages/desktop-client/src/components/select/RecurringSchedulePicker.jsx +++ b/packages/desktop-client/src/components/select/RecurringSchedulePicker.jsx @@ -7,7 +7,7 @@ import { getRecurringDescription } from 'loot-core/src/shared/schedules'; import { useDateFormat } from '../../hooks/useDateFormat'; import { SvgAdd, SvgSubtract } from '../../icons/v0'; import { theme } from '../../style'; -import { Button } from '../common/Button'; +import { Button } from '../common/Button2'; import { Input } from '../common/Input'; import { Menu } from '../common/Menu'; import { Popover } from '../common/Popover'; @@ -241,10 +241,10 @@ function MonthlyPatterns({ config, dispatch }) { buttonStyle={{ flex: 1, marginRight: 10 }} /> <Button - type="bare" + variant="bare" aria-label="Remove recurrence" style={{ padding: 7 }} - onClick={() => + onPress={() => dispatch({ type: 'remove-recurrence', recurrence, @@ -254,10 +254,10 @@ function MonthlyPatterns({ config, dispatch }) { <SvgSubtract style={{ width: 8, height: 8 }} /> </Button> <Button - type="bare" + variant="bare" aria-label="Add recurrence" style={{ padding: 7, marginLeft: 5 }} - onClick={() => dispatch({ type: 'add-recurrence' })} + onPress={() => dispatch({ type: 'add-recurrence' })} > <SvgAdd style={{ width: 10, height: 10 }} /> </Button> @@ -381,7 +381,7 @@ function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) { backgroundColor: theme.tableBackground, ':hover': { backgroundColor: theme.tableBackground }, }} - onClick={() => dispatch({ type: 'add-recurrence' })} + onPress={() => dispatch({ type: 'add-recurrence' })} > Add specific days </Button> @@ -444,10 +444,10 @@ function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) { <div style={{ display: 'flex', marginTop: 15, justifyContent: 'flex-end' }} > - <Button onClick={onClose}>Cancel</Button> + <Button onPress={onClose}>Cancel</Button> <Button - type="primary" - onClick={() => onSave(unparseConfig(config))} + variant="primary" + onPress={() => onSave(unparseConfig(config))} style={{ marginLeft: 10 }} > Apply @@ -472,7 +472,7 @@ export function RecurringSchedulePicker({ value, buttonStyle, onChange }) { <Button ref={triggerRef} style={{ textAlign: 'left', ...buttonStyle }} - onClick={() => setIsOpen(true)} + onPress={() => setIsOpen(true)} > {value ? getRecurringDescription(value, dateFormat) diff --git a/packages/desktop-client/src/components/sidebar/Sidebar.tsx b/packages/desktop-client/src/components/sidebar/Sidebar.tsx index f39629292af86df706218a96b6f92c91dac8ae6d..4f76a71ea73cbc85e292d75d2afd5e3261cc1073 100644 --- a/packages/desktop-client/src/components/sidebar/Sidebar.tsx +++ b/packages/desktop-client/src/components/sidebar/Sidebar.tsx @@ -20,7 +20,7 @@ import { SvgReports, SvgWallet } from '../../icons/v1'; import { SvgCalendar } from '../../icons/v2'; import { useResponsive } from '../../ResponsiveProvider'; import { styles, theme } from '../../style'; -import { Button } from '../common/Button'; +import { Button } from '../common/Button2'; import { InitialFocus } from '../common/InitialFocus'; import { Input } from '../common/Input'; import { Menu } from '../common/Menu'; @@ -238,15 +238,15 @@ function EditableBudgetName() { <> <Button ref={triggerRef} - type="bare" - color={theme.buttonNormalBorder} + variant="bare" style={{ + color: theme.buttonNormalBorder, fontSize: 16, fontWeight: 500, marginLeft: -5, flex: '0 auto', }} - onClick={() => setMenuOpen(true)} + onPress={() => setMenuOpen(true)} > <Text style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}> {budgetName || 'A budget has no name'} diff --git a/packages/desktop-client/src/components/sidebar/ToggleButton.tsx b/packages/desktop-client/src/components/sidebar/ToggleButton.tsx index 68375191e5dc90571d95a4700eb78b32e942a815..bf7c79f5dc84bc3f061787aba79626c1d29f9dda 100644 --- a/packages/desktop-client/src/components/sidebar/ToggleButton.tsx +++ b/packages/desktop-client/src/components/sidebar/ToggleButton.tsx @@ -1,14 +1,14 @@ -import React, { type MouseEventHandler } from 'react'; +import React, { type ComponentPropsWithoutRef } from 'react'; import { SvgPin } from '../../icons/v1'; import { SvgArrowButtonLeft1 } from '../../icons/v2'; import { type CSSProperties, theme } from '../../style'; -import { Button } from '../common/Button'; +import { Button } from '../common/Button2'; import { View } from '../common/View'; type ToggleButtonProps = { isFloating: boolean; - onFloat: MouseEventHandler<HTMLButtonElement>; + onFloat: ComponentPropsWithoutRef<typeof Button>['onPress']; style?: CSSProperties; }; @@ -20,10 +20,10 @@ export function ToggleButton({ return ( <View className="float" style={{ ...style, flexShrink: 0 }}> <Button - type="bare" + variant="bare" aria-label={`${isFloating ? 'Pin' : 'Unpin'} sidebar`} - onClick={onFloat} - color={theme.buttonMenuBorder} + onPress={onFloat} + style={{ color: theme.buttonMenuBorder }} > {isFloating ? ( <SvgPin diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx index e8a88718ea08e0a3f42bd4ea0a1b6e47af61d721..34aa417ae512b4da804a89991df5846f8aa36aaa 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx @@ -61,7 +61,7 @@ import { styles, theme } from '../../style'; import { AccountAutocomplete } from '../autocomplete/AccountAutocomplete'; import { CategoryAutocomplete } from '../autocomplete/CategoryAutocomplete'; import { PayeeAutocomplete } from '../autocomplete/PayeeAutocomplete'; -import { Button } from '../common/Button'; +import { Button } from '../common/Button2'; import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; import { Tooltip } from '../common/Tooltip'; @@ -468,7 +468,7 @@ function HeaderCell({ }} unexposedContent={({ value: cellValue }) => onClick ? ( - <Button type="bare" onClick={onClick} style={style}> + <Button variant="bare" onPress={onClick} style={style}> <UnexposedCellContent value={cellValue} /> {icon === 'asc' && ( <SvgArrowDown width={10} height={10} style={{ marginLeft: 5 }} /> @@ -747,33 +747,36 @@ function PayeeIcons({ ? scheduleData.schedules.find(s => s.id === scheduleId) : null; + const buttonStyle = useMemo( + () => ({ + marginLeft: -5, + marginRight: 2, + width: 23, + height: 23, + color: 'inherit', + }), + [], + ); + + const scheduleIconStyle = useMemo(() => ({ width: 13, height: 13 }), []); + + const transferIconStyle = useMemo(() => ({ width: 10, height: 10 }), []); + if (schedule == null && transferAccount == null) { // Neither a valid scheduled transaction nor a transfer. return null; } - const buttonStyle = { - marginLeft: -5, - marginRight: 2, - width: 23, - height: 23, - color: 'inherit', - }; - - const scheduleIconStyle = { width: 13, height: 13 }; - - const transferIconStyle = { width: 10, height: 10 }; - const recurring = schedule && schedule._date && !!schedule._date.frequency; return ( <> {schedule && ( <Button - type="bare" + variant="bare" + aria-label="See schedule details" style={buttonStyle} - onClick={e => { - e.stopPropagation(); + onPress={() => { onNavigateToSchedule(scheduleId); }} > @@ -786,11 +789,10 @@ function PayeeIcons({ )} {transferAccount && ( <Button - type="bare" - aria-label="Transfer" + variant="bare" + aria-label="See transfer account" style={buttonStyle} - onClick={e => { - e.stopPropagation(); + onPress={() => { if (!isTemporaryId(transaction.id)) { onNavigateToTransferAccount(transferAccount.id); } @@ -1571,18 +1573,18 @@ function TransactionError({ </Text> <View style={{ flex: 1 }} /> <Button - type="normal" + variant="normal" style={{ marginLeft: 15 }} - onClick={onDistributeRemainder} + onPress={onDistributeRemainder} data-testid="distribute-split-button" - disabled={!canDistributeRemainder} + isDisabled={!canDistributeRemainder} > Distribute </Button> <Button - type="primary" + variant="primary" style={{ marginLeft: 10, padding: '4px 10px' }} - onClick={onAddSplit} + onPress={onAddSplit} data-testid="add-split-button" > Add Split @@ -1709,7 +1711,7 @@ function NewTransaction({ > <Button style={{ marginRight: 10, padding: '4px 10px' }} - onClick={() => onClose()} + onPress={() => onClose()} data-testid="cancel-button" > Cancel @@ -1726,9 +1728,9 @@ function NewTransaction({ /> ) : ( <Button - type="primary" + variant="primary" style={{ padding: '4px 10px' }} - onClick={onAdd} + onPress={onAdd} data-testid="add-button" > Add @@ -2500,9 +2502,9 @@ function notesTagFormatter(notes, onNotesTagClick) { return ( <span key={`${validTag}${ti}`}> <Button - type="bare" + variant="bare" key={i} - style={{ + style={({ isHovered }) => ({ display: 'inline-flex', padding: '3px 7px', borderRadius: 16, @@ -2510,13 +2512,11 @@ function notesTagFormatter(notes, onNotesTagClick) { backgroundColor: theme.noteTagBackground, color: theme.noteTagText, cursor: 'pointer', - }} - hoveredStyle={{ - backgroundColor: theme.noteTagBackgroundHover, - color: theme.noteTagText, - }} - onClick={e => { - e.stopPropagation(); + ...(isHovered + ? { backgroundColor: theme.noteTagBackgroundHover } + : {}), + })} + onPress={() => { onNotesTagClick?.(validTag); }} > diff --git a/upcoming-release-notes/2984.md b/upcoming-release-notes/2984.md new file mode 100644 index 0000000000000000000000000000000000000000..9dae3c74012379b2d1057ef87a9e8c8a15eb06de --- /dev/null +++ b/upcoming-release-notes/2984.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [joel-jeremy] +--- + +Use new react-aria-components based Button on sidebar, notifications, transactions, recurring schedule picker, etc.