From e507b8ff436e344c4377d3f285127d23d61ecead Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com> Date: Thu, 19 Sep 2024 16:12:57 -0700 Subject: [PATCH] Fix React Aria Button hover styles (#3453) * Fiox hover styles and use className instead of inline to prepare for future css migration * Release notes * Cleanup * Update edit rule hover style --- .../src/components/Notifications.tsx | 48 ++++----- .../src/components/Titlebar.tsx | 34 ++++--- .../src/components/common/Button2.tsx | 8 +- .../src/components/common/Link.tsx | 11 ++- .../src/components/common/Select.tsx | 13 +-- .../components/mobile/MobileBackButton.tsx | 30 +++--- .../components/mobile/budget/BudgetTable.jsx | 97 ++++++++++--------- .../src/components/modals/EditRuleModal.jsx | 7 +- .../modals/ImportTransactionsModal.jsx | 2 +- .../modals/ReportBudgetMonthMenuModal.tsx | 20 ++-- .../modals/RolloverBudgetMonthMenuModal.tsx | 20 ++-- .../src/components/reports/Header.tsx | 2 +- .../select/RecurringSchedulePicker.tsx | 6 +- .../src/components/settings/Format.tsx | 6 +- .../src/components/settings/Themes.tsx | 8 +- .../transactions/TransactionsTable.jsx | 27 +++--- upcoming-release-notes/3453.md | 6 ++ 17 files changed, 190 insertions(+), 155 deletions(-) create mode 100644 upcoming-release-notes/3453.md diff --git a/packages/desktop-client/src/components/Notifications.tsx b/packages/desktop-client/src/components/Notifications.tsx index 4fe464a3e..6e65af40e 100644 --- a/packages/desktop-client/src/components/Notifications.tsx +++ b/packages/desktop-client/src/components/Notifications.tsx @@ -7,6 +7,8 @@ import React, { } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { css } from 'glamor'; + import { removeNotification } from 'loot-core/client/actions'; import { type State } from 'loot-core/src/client/state-types'; import type { NotificationWithId } from 'loot-core/src/client/state-types/notifications'; @@ -200,29 +202,29 @@ function Notification({ onRemove(); setLoading(false); }} - style={({ isHovered, isPressed }) => ({ - backgroundColor: 'transparent', - border: `1px solid ${ - positive - ? theme.noticeBorder - : error - ? theme.errorBorder - : theme.warningBorder - }`, - color: 'currentColor', - ...styles.mediumText, - flexShrink: 0, - ...(isHovered || isPressed - ? { - backgroundColor: positive - ? theme.noticeBackground - : error - ? theme.errorBackground - : theme.warningBackground, - } - : {}), - ...narrowStyle, - })} + className={String( + css({ + backgroundColor: 'transparent', + border: `1px solid ${ + positive + ? theme.noticeBorder + : error + ? theme.errorBorder + : theme.warningBorder + }`, + color: 'currentColor', + ...styles.mediumText, + flexShrink: 0, + '&[data-hovered], &[data-pressed]': { + backgroundColor: positive + ? theme.noticeBackground + : error + ? theme.errorBackground + : theme.warningBackground, + }, + ...narrowStyle, + }), + )} > {button.title} </ButtonWithLoading> diff --git a/packages/desktop-client/src/components/Titlebar.tsx b/packages/desktop-client/src/components/Titlebar.tsx index c283886ea..7ef7ad41d 100644 --- a/packages/desktop-client/src/components/Titlebar.tsx +++ b/packages/desktop-client/src/components/Titlebar.tsx @@ -2,6 +2,8 @@ import React, { useState, useEffect } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { Routes, Route, useLocation } from 'react-router-dom'; +import { css } from 'glamor'; + import * as Platform from 'loot-core/src/client/platform'; import * as queries from 'loot-core/src/client/queries'; import { listen } from 'loot-core/src/platform/client/fetch'; @@ -200,21 +202,23 @@ function SyncButton({ style, isMobile = false }: SyncButtonProps) { <Button variant="bare" aria-label="Sync" - style={({ isHovered, isPressed }) => ({ - ...(isMobile - ? { - ...style, - WebkitAppRegion: 'none', - ...mobileIconStyle, - } - : { - ...style, - WebkitAppRegion: 'none', - color: desktopColor, - }), - ...(isHovered ? hoveredStyle : {}), - ...(isPressed ? activeStyle : {}), - })} + className={String( + css({ + ...(isMobile + ? { + ...style, + WebkitAppRegion: 'none', + ...mobileIconStyle, + } + : { + ...style, + WebkitAppRegion: 'none', + color: desktopColor, + }), + '&[data-hovered]': hoveredStyle, + '&[data-pressed]': activeStyle, + }), + )} onPress={sync} > {isMobile ? ( diff --git a/packages/desktop-client/src/components/common/Button2.tsx b/packages/desktop-client/src/components/common/Button2.tsx index 0fb4860ff..7921dbccb 100644 --- a/packages/desktop-client/src/components/common/Button2.tsx +++ b/packages/desktop-client/src/components/common/Button2.tsx @@ -173,8 +173,12 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>( transition: 'box-shadow .25s', WebkitAppRegion: 'no-drag', ...styles.smallText, - ...(renderProps.isDisabled ? {} : { ':hover': hoveredStyle }), - ...(renderProps.isDisabled ? {} : { ':active': activeStyle }), + ...(renderProps.isDisabled + ? {} + : { + '&[data-hovered]': hoveredStyle, + '&[data-pressed]': activeStyle, + }), ...(Icon ? { paddingLeft: 0 } : {}), }), ); diff --git a/packages/desktop-client/src/components/common/Link.tsx b/packages/desktop-client/src/components/common/Link.tsx index a945e9557..ebe1f3552 100644 --- a/packages/desktop-client/src/components/common/Link.tsx +++ b/packages/desktop-client/src/components/common/Link.tsx @@ -95,10 +95,13 @@ const ButtonLink = ({ to, style, activeStyle, ...props }: ButtonLinkProps) => { const match = useMatch({ path }); return ( <Button - style={({ isPressed }) => ({ - ...style, - ...(match || isPressed ? activeStyle : {}), - })} + className={String( + css({ + ...style, + '&[data-pressed]': activeStyle, + ...(match ? activeStyle : {}), + }), + )} {...props} variant={props.buttonVariant} onPress={e => { diff --git a/packages/desktop-client/src/components/common/Select.tsx b/packages/desktop-client/src/components/common/Select.tsx index 27b674791..c9fc00b41 100644 --- a/packages/desktop-client/src/components/common/Select.tsx +++ b/packages/desktop-client/src/components/common/Select.tsx @@ -1,5 +1,7 @@ import { useRef, useState } from 'react'; +import { css } from 'glamor'; + import { SvgExpandArrow } from '../../icons/v0'; import { type CSSProperties } from '../../style'; @@ -25,7 +27,7 @@ type SelectProps<Value> = { onChange?: (newValue: Value) => void; disabled?: boolean; disabledKeys?: Value[]; - buttonStyle?: CSSProperties; + style?: CSSProperties; }; /** @@ -50,7 +52,7 @@ export function Select<const Value = string>({ onChange, disabled = false, disabledKeys = [], - buttonStyle = {}, + style = {}, }: SelectProps<Value>) { const targetOption = options .filter(isValueOption) @@ -69,12 +71,7 @@ export function Select<const Value = string>({ onPress={() => { setIsOpen(true); }} - style={({ isHovered }) => ({ - ...buttonStyle, - ...(isHovered - ? { backgroundColor: bare ? 'transparent' : undefined } - : {}), - })} + className={String(css(style))} > <View style={{ diff --git a/packages/desktop-client/src/components/mobile/MobileBackButton.tsx b/packages/desktop-client/src/components/mobile/MobileBackButton.tsx index aa1604bb6..b0b69f9b4 100644 --- a/packages/desktop-client/src/components/mobile/MobileBackButton.tsx +++ b/packages/desktop-client/src/components/mobile/MobileBackButton.tsx @@ -1,5 +1,7 @@ import React, { type ComponentPropsWithoutRef } from 'react'; +import { css } from 'glamor'; + import { useNavigate } from '../../hooks/useNavigate'; import { SvgCheveronLeft } from '../../icons/v1'; import { styles, theme } from '../../style'; @@ -17,20 +19,20 @@ export function MobileBackButton({ return ( <Button variant="bare" - style={({ isHovered }) => ({ - color: theme.mobileHeaderText, - justifyContent: 'center', - margin: 10, - paddingLeft: 5, - paddingRight: 3, - ...(isHovered - ? { - color: theme.mobileHeaderText, - background: theme.mobileHeaderTextHover, - } - : {}), - ...style, - })} + className={String( + css({ + color: theme.mobileHeaderText, + justifyContent: 'center', + margin: 10, + paddingLeft: 5, + paddingRight: 3, + '&[data-hovered]': { + color: theme.mobileHeaderText, + background: theme.mobileHeaderTextHover, + }, + ...style, + }), + )} onPress={onPress || (() => navigate(-1))} {...props} > diff --git a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx index 6ecc225e7..0bee9e2d0 100644 --- a/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx +++ b/packages/desktop-client/src/components/mobile/budget/BudgetTable.jsx @@ -2,6 +2,7 @@ import React, { memo, useCallback, useRef } from 'react'; import { useDispatch } from 'react-redux'; import { AutoTextSize } from 'auto-text-size'; +import { css } from 'glamor'; import memoizeOne from 'memoize-one'; import { collapseModals, pushModal } from 'loot-core/client/actions'; @@ -780,14 +781,16 @@ const ExpenseGroupHeader = memo(function ExpenseGroupHeader({ > <Button variant="bare" - style={({ isPressed, isHovered }) => ({ - flexShrink: 0, - color: theme.pageTextSubdued, - ...styles.noTapHighlight, - ...(isPressed || isHovered - ? { backgroundColor: 'transparent' } - : {}), - })} + className={String( + css({ + flexShrink: 0, + color: theme.pageTextSubdued, + ...styles.noTapHighlight, + '&[data-hovered], &[data-pressed]': { + backgroundColor: 'transparent', + }, + }), + )} onPress={() => onToggleCollapse?.(group.id)} > <SvgExpandArrow @@ -974,14 +977,16 @@ const IncomeGroupHeader = memo(function IncomeGroupHeader({ > <Button variant="bare" - style={({ isPressed, isHovered }) => ({ - flexShrink: 0, - color: theme.pageTextSubdued, - ...styles.noTapHighlight, - ...(isPressed || isHovered - ? { backgroundColor: 'transparent' } - : {}), - })} + className={String( + css({ + flexShrink: 0, + color: theme.pageTextSubdued, + ...styles.noTapHighlight, + '&[data-hovered], &[data-pressed]': { + backgroundColor: 'transparent', + }, + }), + )} onPress={() => onToggleCollapse?.(group.id)} > <SvgExpandArrow @@ -1600,11 +1605,13 @@ export function BudgetTable({ leftContent={ <Button variant="bare" - style={({ isPressed, isHovered }) => ({ - color: theme.mobileHeaderText, - margin: 10, - ...(isPressed || isHovered ? noBackgroundColorStyle : {}), - })} + className={String( + css({ + color: theme.mobileHeaderText, + margin: 10, + '&[data-hovered], &[data-pressed]': noBackgroundColorStyle, + }), + )} onPress={onOpenBudgetPageMenu} > <SvgLogo width="20" height="20" /> @@ -1913,18 +1920,18 @@ function MonthSelector({ onPrevMonth(); } }} - style={({ isHovered }) => ({ - ...styles.noTapHighlight, - ...arrowButtonStyle, - opacity: prevEnabled ? 1 : 0.6, - color: theme.mobileHeaderText, - ...(isHovered - ? { - color: theme.mobileHeaderText, - background: theme.mobileHeaderTextHover, - } - : {}), - })} + className={String( + css({ + ...styles.noTapHighlight, + ...arrowButtonStyle, + opacity: prevEnabled ? 1 : 0.6, + color: theme.mobileHeaderText, + '&[data-hovered]': { + color: theme.mobileHeaderText, + background: theme.mobileHeaderTextHover, + }, + }), + )} > <SvgArrowThinLeft width="15" height="15" style={{ margin: -5 }} /> </Button> @@ -1952,18 +1959,18 @@ function MonthSelector({ onNextMonth(); } }} - style={({ isHovered }) => ({ - ...styles.noTapHighlight, - ...arrowButtonStyle, - opacity: nextEnabled ? 1 : 0.6, - color: theme.mobileHeaderText, - ...(isHovered - ? { - color: theme.mobileHeaderText, - background: theme.mobileHeaderTextHover, - } - : {}), - })} + className={String( + css({ + ...styles.noTapHighlight, + ...arrowButtonStyle, + opacity: nextEnabled ? 1 : 0.6, + color: theme.mobileHeaderText, + '&[data-hovered]': { + color: theme.mobileHeaderText, + background: theme.mobileHeaderTextHover, + }, + }), + )} > <SvgArrowThinRight width="15" height="15" style={{ margin: -5 }} /> </Button> diff --git a/packages/desktop-client/src/components/modals/EditRuleModal.jsx b/packages/desktop-client/src/components/modals/EditRuleModal.jsx index 51a46ef54..ea6a88279 100644 --- a/packages/desktop-client/src/components/modals/EditRuleModal.jsx +++ b/packages/desktop-client/src/components/modals/EditRuleModal.jsx @@ -95,7 +95,10 @@ export function FieldSelect({ fields, style, value, onChange }) { options={fields} value={value} onChange={onChange} - buttonStyle={{ color: theme.pageTextPositive }} + style={{ + color: theme.pageTextPositive, + '&[data-hovered]': { color: theme.pageTextPositive }, + }} /> </View> ); @@ -135,7 +138,7 @@ export function OpSelect({ options={opOptions} value={value} onChange={value => onChange('op', value)} - buttonStyle={style} + style={style} /> </View> ); diff --git a/packages/desktop-client/src/components/modals/ImportTransactionsModal.jsx b/packages/desktop-client/src/components/modals/ImportTransactionsModal.jsx index 00ffdc971..feaf188bf 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactionsModal.jsx +++ b/packages/desktop-client/src/components/modals/ImportTransactionsModal.jsx @@ -590,7 +590,7 @@ function SelectField({ ]} value={value === null ? 'choose-field' : value} onChange={onChange} - buttonStyle={style} + style={style} /> ); } diff --git a/packages/desktop-client/src/components/modals/ReportBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/ReportBudgetMonthMenuModal.tsx index ae5e9b645..9ee19db94 100644 --- a/packages/desktop-client/src/components/modals/ReportBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/ReportBudgetMonthMenuModal.tsx @@ -1,6 +1,8 @@ // @ts-strict-ignore import React, { useState } from 'react'; +import { css } from 'glamor'; + import * as monthUtils from 'loot-core/src/shared/months'; import { useNotes } from '../../hooks/useNotes'; @@ -117,15 +119,15 @@ export function ReportBudgetMonthMenuModal({ <View> <Button variant="bare" - style={({ isPressed, isHovered }) => ({ - ...buttonStyle, - ...(isPressed || isHovered - ? { - backgroundColor: 'transparent', - color: buttonStyle.color, - } - : {}), - })} + className={String( + css({ + ...buttonStyle, + '&[data-pressed], &[data-hovered]': { + backgroundColor: 'transparent', + color: buttonStyle.color, + }, + }), + )} onPress={onShowMore} > {!showMore ? ( diff --git a/packages/desktop-client/src/components/modals/RolloverBudgetMonthMenuModal.tsx b/packages/desktop-client/src/components/modals/RolloverBudgetMonthMenuModal.tsx index a8b34852c..c35ea9ab5 100644 --- a/packages/desktop-client/src/components/modals/RolloverBudgetMonthMenuModal.tsx +++ b/packages/desktop-client/src/components/modals/RolloverBudgetMonthMenuModal.tsx @@ -1,6 +1,8 @@ // @ts-strict-ignore import React, { useState } from 'react'; +import { css } from 'glamor'; + import * as monthUtils from 'loot-core/src/shared/months'; import { useNotes } from '../../hooks/useNotes'; @@ -117,15 +119,15 @@ export function RolloverBudgetMonthMenuModal({ <View> <Button variant="bare" - style={({ isPressed, isHovered }) => ({ - ...buttonStyle, - ...(isPressed || isHovered - ? { - backgroundColor: 'transparent', - color: buttonStyle.color, - } - : {}), - })} + className={String( + css({ + ...buttonStyle, + '&[data-pressed], &[data-hovered]': { + backgroundColor: 'transparent', + color: buttonStyle.color, + }, + }), + )} onPress={onShowMore} > {!showMore ? ( diff --git a/packages/desktop-client/src/components/reports/Header.tsx b/packages/desktop-client/src/components/reports/Header.tsx index 1eb9fe0ab..b1aad0805 100644 --- a/packages/desktop-client/src/components/reports/Header.tsx +++ b/packages/desktop-client/src/components/reports/Header.tsx @@ -134,7 +134,7 @@ export function Header({ } value={end} options={allMonths.map(({ name, pretty }) => [name, pretty])} - buttonStyle={{ marginRight: 10 }} + style={{ marginRight: 10 }} /> </View> diff --git a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx index eb7c28228..72eb55559 100644 --- a/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx +++ b/packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx @@ -294,7 +294,7 @@ function MonthlyPatterns({ value: parsePatternValue(value), }) } - buttonStyle={{ flex: 1, marginRight: 10 }} + style={{ flex: 1, marginRight: 10 }} /> <Select options={[ @@ -311,7 +311,7 @@ function MonthlyPatterns({ value, }); }} - buttonStyle={{ flex: 1, marginRight: 10 }} + style={{ flex: 1, marginRight: 10 }} /> <Button variant="bare" @@ -457,7 +457,7 @@ function RecurringScheduleTooltip({ options={FREQUENCY_OPTIONS.map(opt => [opt.id, opt.name])} value={config.frequency} onChange={value => updateField('frequency', value)} - buttonStyle={{ marginRight: 5 }} + style={{ marginRight: 5 }} /> {config.frequency === 'monthly' && (config.patterns == null || config.patterns.length === 0) ? ( diff --git a/packages/desktop-client/src/components/settings/Format.tsx b/packages/desktop-client/src/components/settings/Format.tsx index d3cbfd8f8..dda0ca350 100644 --- a/packages/desktop-client/src/components/settings/Format.tsx +++ b/packages/desktop-client/src/components/settings/Format.tsx @@ -96,7 +96,7 @@ export function FormatSettings() { f.value, String(hideFraction) === 'true' ? f.labelNoFraction : f.label, ])} - buttonStyle={selectButtonStyle} + style={selectButtonStyle} /> <Text style={{ display: 'flex' }}> @@ -116,7 +116,7 @@ export function FormatSettings() { value={dateFormat} onChange={format => setDateFormatPref(format)} options={dateFormats.map(f => [f.value, f.label])} - buttonStyle={selectButtonStyle} + style={selectButtonStyle} /> </Column> @@ -125,7 +125,7 @@ export function FormatSettings() { value={firstDayOfWeekIdx} onChange={idx => setFirstDayOfWeekIdxPref(idx)} options={daysOfWeek.map(f => [f.value, f.label])} - buttonStyle={selectButtonStyle} + style={selectButtonStyle} /> </Column> </View> diff --git a/packages/desktop-client/src/components/settings/Themes.tsx b/packages/desktop-client/src/components/settings/Themes.tsx index 10fff9480..3eeebfb53 100644 --- a/packages/desktop-client/src/components/settings/Themes.tsx +++ b/packages/desktop-client/src/components/settings/Themes.tsx @@ -62,8 +62,8 @@ export function ThemeSettings() { }} value={theme} options={themeOptions} - buttonStyle={{ - ':hover': { + style={{ + '&[data-hovered]': { backgroundColor: themeStyle.buttonNormalBackgroundHover, }, }} @@ -77,8 +77,8 @@ export function ThemeSettings() { }} value={darkTheme} options={darkThemeOptions} - buttonStyle={{ - ':hover': { + style={{ + '&[data-hovered]': { backgroundColor: themeStyle.buttonNormalBackgroundHover, }, }} diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx index d205eb85d..36423d2b5 100644 --- a/packages/desktop-client/src/components/transactions/TransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/TransactionsTable.jsx @@ -18,6 +18,7 @@ import { parseISO, isValid as isDateValid, } from 'date-fns'; +import { css } from 'glamor'; import { pushModal } from 'loot-core/client/actions'; import { useCachedSchedules } from 'loot-core/src/client/data-hooks/schedules'; @@ -2528,18 +2529,20 @@ function notesTagFormatter(notes, onNotesTagClick) { <Button variant="bare" key={i} - style={({ isHovered }) => ({ - display: 'inline-flex', - padding: '3px 7px', - borderRadius: 16, - userSelect: 'none', - backgroundColor: theme.noteTagBackground, - color: theme.noteTagText, - cursor: 'pointer', - ...(isHovered - ? { backgroundColor: theme.noteTagBackgroundHover } - : {}), - })} + className={String( + css({ + display: 'inline-flex', + padding: '3px 7px', + borderRadius: 16, + userSelect: 'none', + backgroundColor: theme.noteTagBackground, + color: theme.noteTagText, + cursor: 'pointer', + '&[data-hovered]': { + backgroundColor: theme.noteTagBackgroundHover, + }, + }), + )} onPress={() => { onNotesTagClick?.(validTag); }} diff --git a/upcoming-release-notes/3453.md b/upcoming-release-notes/3453.md new file mode 100644 index 000000000..42a848f2a --- /dev/null +++ b/upcoming-release-notes/3453.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [joel-jeremy] +--- + +Fix react-aria-components Button hover styles -- GitLab