From 5f16349a19ad9a7e32c02b2eebbe4d3598ed537a Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins <matiss@mja.lv> Date: Tue, 21 May 2024 21:40:55 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(tooltip)=20refactoring=20?= =?UTF-8?q?to=20react-aria=20(vol.7)=20(#2778)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/MenuButton.tsx | 41 +++-- .../src/components/common/Popover.tsx | 2 +- .../src/components/reports/GraphButton.tsx | 26 +-- .../src/components/reports/ReportSidebar.tsx | 141 ++++++++------- .../src/components/reports/SaveReport.tsx | 52 ++++-- .../components/reports/SaveReportChoose.tsx | 13 +- .../components/reports/SaveReportDelete.tsx | 10 +- .../src/components/reports/SaveReportMenu.tsx | 21 +-- .../src/components/reports/SaveReportName.tsx | 7 +- .../reports/reports/CustomReportListCards.tsx | 164 ++++++++++-------- upcoming-release-notes/2778.md | 6 + 11 files changed, 251 insertions(+), 232 deletions(-) create mode 100644 upcoming-release-notes/2778.md diff --git a/packages/desktop-client/src/components/common/MenuButton.tsx b/packages/desktop-client/src/components/common/MenuButton.tsx index b707ef6ba..452c6b642 100644 --- a/packages/desktop-client/src/components/common/MenuButton.tsx +++ b/packages/desktop-client/src/components/common/MenuButton.tsx @@ -1,24 +1,33 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { SvgDotsHorizontalTriple } from '../../icons/v1'; import { type CSSProperties } from '../../style'; import { Button } from './Button'; -export function MenuButton({ - onClick, - style, -}: { +type MenuButtonProps = { onClick: () => void; style?: CSSProperties; -}) { - return ( - <Button type="bare" onClick={onClick} aria-label="Menu" style={style}> - <SvgDotsHorizontalTriple - width={15} - height={15} - style={{ transform: 'rotateZ(90deg)' }} - /> - </Button> - ); -} +}; + +export const MenuButton = forwardRef<HTMLButtonElement, MenuButtonProps>( + ({ onClick, style }, ref) => { + return ( + <Button + ref={ref} + type="bare" + onClick={onClick} + aria-label="Menu" + style={style} + > + <SvgDotsHorizontalTriple + width={15} + height={15} + style={{ transform: 'rotateZ(90deg)' }} + /> + </Button> + ); + }, +); + +MenuButton.displayName = 'MenuButton'; diff --git a/packages/desktop-client/src/components/common/Popover.tsx b/packages/desktop-client/src/components/common/Popover.tsx index 834c24e1c..2eb575e00 100644 --- a/packages/desktop-client/src/components/common/Popover.tsx +++ b/packages/desktop-client/src/components/common/Popover.tsx @@ -15,7 +15,7 @@ export const Popover = ({ return ( <ReactAriaPopover placement="bottom end" - offset={0} + offset={1} className={`${css({ ...styles.tooltip, ...styles.lightScrollbar, diff --git a/packages/desktop-client/src/components/reports/GraphButton.tsx b/packages/desktop-client/src/components/reports/GraphButton.tsx index f648ca14e..f62b771eb 100644 --- a/packages/desktop-client/src/components/reports/GraphButton.tsx +++ b/packages/desktop-client/src/components/reports/GraphButton.tsx @@ -1,10 +1,9 @@ import React, { type HTMLProps } from 'react'; -import { type CSSProperties, theme } from '../../style'; +import { type CSSProperties, styles, theme } from '../../style'; import { Button } from '../common/Button'; -import { HoverTarget } from '../common/HoverTarget'; import { Text } from '../common/Text'; -import { Tooltip } from '../tooltips'; +import { Tooltip } from '../common/Tooltip'; type GraphButtonProps = HTMLProps<HTMLButtonElement> & { selected?: boolean; @@ -23,21 +22,10 @@ export const GraphButton = ({ disabled, }: GraphButtonProps) => { return ( - <HoverTarget - style={{ flexShrink: 0 }} - renderContent={() => ( - <Tooltip - position="bottom-left" - style={{ - lineHeight: 1.5, - padding: '6px 10px', - backgroundColor: theme.menuBackground, - color: theme.menuItemText, - }} - > - <Text>{title}</Text> - </Tooltip> - )} + <Tooltip + placement="bottom start" + content={<Text>{title}</Text>} + style={{ ...styles.tooltip, lineHeight: 1.5, padding: '6px 10px' }} > <Button type="bare" @@ -52,6 +40,6 @@ export const GraphButton = ({ > {children} </Button> - </HoverTarget> + </Tooltip> ); }; diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.tsx b/packages/desktop-client/src/components/reports/ReportSidebar.tsx index 33fbe5c12..35aba3f7b 100644 --- a/packages/desktop-client/src/components/reports/ReportSidebar.tsx +++ b/packages/desktop-client/src/components/reports/ReportSidebar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useRef, useState } from 'react'; import * as monthUtils from 'loot-core/src/shared/months'; import { type CategoryEntity } from 'loot-core/types/models/category'; @@ -9,10 +9,10 @@ import { type LocalPrefs } from 'loot-core/types/prefs'; import { theme } from '../../style/theme'; import { Button } from '../common/Button'; import { Menu } from '../common/Menu'; +import { Popover } from '../common/Popover'; import { Select } from '../common/Select'; import { Text } from '../common/Text'; import { View } from '../common/View'; -import { Tooltip } from '../tooltips'; import { CategorySelector } from './CategorySelector'; import { defaultsList } from './disabledList'; @@ -80,6 +80,7 @@ export function ReportSidebar({ firstDayOfWeekIdx, }: ReportSidebarProps) { const [menuOpen, setMenuOpen] = useState(false); + const triggerRef = useRef(null); const onSelectRange = (cond: string) => { setSessionReport('dateRange', cond); onReportChange({ type: 'modify' }); @@ -246,6 +247,7 @@ export function ReportSidebar({ > <Text style={{ width: 50, textAlign: 'right', marginRight: 5 }} /> <Button + ref={triggerRef} onClick={() => { setMenuOpen(true); }} @@ -255,78 +257,71 @@ export function ReportSidebar({ }} > Options - {menuOpen && ( - <Tooltip - position="bottom-left" - style={{ padding: 0 }} - onClose={() => { - setMenuOpen(false); - }} - > - <Menu - onMenuSelect={type => { - onReportChange({ type: 'modify' }); - - if (type === 'show-hidden-categories') { - setSessionReport( - 'showHiddenCategories', - !customReportItems.showHiddenCategories, - ); - setShowHiddenCategories( - !customReportItems.showHiddenCategories, - ); - } else if (type === 'show-off-budget') { - setSessionReport( - 'showOffBudget', - !customReportItems.showOffBudget, - ); - setShowOffBudget(!customReportItems.showOffBudget); - } else if (type === 'show-empty-items') { - setSessionReport( - 'showEmpty', - !customReportItems.showEmpty, - ); - setShowEmpty(!customReportItems.showEmpty); - } else if (type === 'show-uncategorized') { - setSessionReport( - 'showUncategorized', - !customReportItems.showUncategorized, - ); - setShowUncategorized( - !customReportItems.showUncategorized, - ); - } - }} - items={[ - { - name: 'show-hidden-categories', - text: 'Show hidden categories', - tooltip: 'Show hidden categories', - toggle: customReportItems.showHiddenCategories, - }, - { - name: 'show-empty-items', - text: 'Show empty rows', - tooltip: 'Show rows that are zero or blank', - toggle: customReportItems.showEmpty, - }, - { - name: 'show-off-budget', - text: 'Show off budget', - tooltip: 'Show off budget accounts', - toggle: customReportItems.showOffBudget, - }, - { - name: 'show-uncategorized', - text: 'Show uncategorized', - tooltip: 'Show uncategorized transactions', - toggle: customReportItems.showUncategorized, - }, - ]} - /> - </Tooltip> - )} </Button> + + <Popover + triggerRef={triggerRef} + placement="bottom start" + isOpen={menuOpen} + onOpenChange={() => setMenuOpen(false)} + > + <Menu + onMenuSelect={type => { + onReportChange({ type: 'modify' }); + + if (type === 'show-hidden-categories') { + setSessionReport( + 'showHiddenCategories', + !customReportItems.showHiddenCategories, + ); + setShowHiddenCategories( + !customReportItems.showHiddenCategories, + ); + } else if (type === 'show-off-budget') { + setSessionReport( + 'showOffBudget', + !customReportItems.showOffBudget, + ); + setShowOffBudget(!customReportItems.showOffBudget); + } else if (type === 'show-empty-items') { + setSessionReport('showEmpty', !customReportItems.showEmpty); + setShowEmpty(!customReportItems.showEmpty); + } else if (type === 'show-uncategorized') { + setSessionReport( + 'showUncategorized', + !customReportItems.showUncategorized, + ); + setShowUncategorized(!customReportItems.showUncategorized); + } + }} + items={[ + { + name: 'show-hidden-categories', + text: 'Show hidden categories', + tooltip: 'Show hidden categories', + toggle: customReportItems.showHiddenCategories, + }, + { + name: 'show-empty-items', + text: 'Show empty rows', + tooltip: 'Show rows that are zero or blank', + toggle: customReportItems.showEmpty, + }, + { + name: 'show-off-budget', + text: 'Show off budget', + tooltip: 'Show off budget accounts', + toggle: customReportItems.showOffBudget, + }, + { + name: 'show-uncategorized', + text: 'Show uncategorized', + tooltip: 'Show uncategorized transactions', + toggle: customReportItems.showUncategorized, + }, + ]} + /> + </Popover> </View> <View style={{ diff --git a/packages/desktop-client/src/components/reports/SaveReport.tsx b/packages/desktop-client/src/components/reports/SaveReport.tsx index d5edb18d1..185919f0e 100644 --- a/packages/desktop-client/src/components/reports/SaveReport.tsx +++ b/packages/desktop-client/src/components/reports/SaveReport.tsx @@ -1,4 +1,4 @@ -import React, { createRef, useState } from 'react'; +import React, { createRef, useRef, useState } from 'react'; import { useReports } from 'loot-core/client/data-hooks/reports'; import { send, sendCatch } from 'loot-core/src/platform/client/fetch'; @@ -6,6 +6,7 @@ import { type CustomReportEntity } from 'loot-core/src/types/models'; import { SvgExpandArrow } from '../../icons/v0'; import { Button } from '../common/Button'; +import { Popover } from '../common/Popover'; import { Text } from '../common/Text'; import { View } from '../common/View'; @@ -34,6 +35,7 @@ export function SaveReport({ onReportChange, }: SaveReportProps) { const listReports = useReports(); + const triggerRef = useRef(null); const [deleteMenuOpen, setDeleteMenuOpen] = useState(false); const [nameMenuOpen, setNameMenuOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false); @@ -156,6 +158,7 @@ export function SaveReport({ }} > <Button + ref={triggerRef} type="bare" onClick={() => { setMenuOpen(true); @@ -175,17 +178,27 @@ export function SaveReport({ {savedStatus === 'modified' && <Text>(modified) </Text>} <SvgExpandArrow width={8} height={8} style={{ marginRight: 5 }} /> </Button> - {menuOpen && ( + + <Popover + triggerRef={triggerRef} + isOpen={menuOpen} + onOpenChange={() => setMenuOpen(false)} + style={{ width: 150 }} + > <SaveReportMenu - onClose={() => setMenuOpen(false)} onMenuSelect={onMenuSelect} savedStatus={savedStatus} listReports={listReports && listReports.length} /> - )} - {nameMenuOpen && ( + </Popover> + + <Popover + triggerRef={triggerRef} + isOpen={nameMenuOpen} + onOpenChange={() => setNameMenuOpen(false)} + style={{ width: 325 }} + > <SaveReportName - onClose={() => setNameMenuOpen(false)} menuItem={menuItem} name={newName} setName={setNewName} @@ -193,20 +206,29 @@ export function SaveReport({ onAddUpdate={onAddUpdate} err={err} /> - )} - {chooseMenuOpen && ( - <SaveReportChoose - onApply={onApply} - onClose={() => setChooseMenuOpen(false)} - /> - )} - {deleteMenuOpen && ( + </Popover> + + <Popover + triggerRef={triggerRef} + isOpen={chooseMenuOpen} + onOpenChange={() => setChooseMenuOpen(false)} + style={{ padding: 15 }} + > + <SaveReportChoose onApply={onApply} /> + </Popover> + + <Popover + triggerRef={triggerRef} + isOpen={deleteMenuOpen} + onOpenChange={() => setDeleteMenuOpen(false)} + style={{ width: 275, padding: 15 }} + > <SaveReportDelete onDelete={onDelete} onClose={() => setDeleteMenuOpen(false)} name={report.name} /> - )} + </Popover> </View> ); } diff --git a/packages/desktop-client/src/components/reports/SaveReportChoose.tsx b/packages/desktop-client/src/components/reports/SaveReportChoose.tsx index 2a6b3f123..0b747ee6e 100644 --- a/packages/desktop-client/src/components/reports/SaveReportChoose.tsx +++ b/packages/desktop-client/src/components/reports/SaveReportChoose.tsx @@ -5,15 +5,13 @@ import { Button } from '../common/Button'; import { Stack } from '../common/Stack'; import { Text } from '../common/Text'; import { View } from '../common/View'; -import { Tooltip } from '../tooltips'; import { GenericInput } from '../util/GenericInput'; type SaveReportChooseProps = { onApply: (cond: string) => void; - onClose: () => void; }; -export function SaveReportChoose({ onApply, onClose }: SaveReportChooseProps) { +export function SaveReportChoose({ onApply }: SaveReportChooseProps) { const inputRef = createRef<HTMLInputElement>(); const [err, setErr] = useState(''); const [value, setValue] = useState(''); @@ -25,12 +23,7 @@ export function SaveReportChoose({ onApply, onClose }: SaveReportChooseProps) { }); return ( - <Tooltip - position="bottom-right" - style={{ padding: 15, color: theme.menuItemText }} - width={275} - onClose={onClose} - > + <> <form> <View style={{ flexDirection: 'row', align: 'center' }}> <Text style={{ userSelect: 'none', flex: 1 }}>Choose Report</Text> @@ -77,6 +70,6 @@ export function SaveReportChoose({ onApply, onClose }: SaveReportChooseProps) { ) : ( <View /> )} - </Tooltip> + </> ); } diff --git a/packages/desktop-client/src/components/reports/SaveReportDelete.tsx b/packages/desktop-client/src/components/reports/SaveReportDelete.tsx index a18a68ede..3a5b95fed 100644 --- a/packages/desktop-client/src/components/reports/SaveReportDelete.tsx +++ b/packages/desktop-client/src/components/reports/SaveReportDelete.tsx @@ -5,7 +5,6 @@ import { Button } from '../common/Button'; import { Stack } from '../common/Stack'; import { Text } from '../common/Text'; import { View } from '../common/View'; -import { Tooltip } from '../tooltips'; type SaveReportDeleteProps = { onDelete: () => void; @@ -19,12 +18,7 @@ export function SaveReportDelete({ name, }: SaveReportDeleteProps) { return ( - <Tooltip - position="bottom-right" - style={{ padding: 15, color: theme.menuItemText }} - width={275} - onClose={onClose} - > + <> <View style={{ align: 'center' }}> <Text style={{ color: theme.errorText, marginBottom: 5 }}> Do you want to delete report: @@ -46,6 +40,6 @@ export function SaveReportDelete({ No </Button> </Stack> - </Tooltip> + </> ); } diff --git a/packages/desktop-client/src/components/reports/SaveReportMenu.tsx b/packages/desktop-client/src/components/reports/SaveReportMenu.tsx index d2ba778ae..d6eb390f0 100644 --- a/packages/desktop-client/src/components/reports/SaveReportMenu.tsx +++ b/packages/desktop-client/src/components/reports/SaveReportMenu.tsx @@ -1,15 +1,12 @@ import React, { type ComponentPropsWithoutRef } from 'react'; import { Menu } from '../common/Menu'; -import { MenuTooltip } from '../common/MenuTooltip'; export function SaveReportMenu({ - onClose, onMenuSelect, savedStatus, listReports, }: { - onClose: () => void; onMenuSelect: (item: string) => void; savedStatus: string; listReports: number; @@ -68,17 +65,11 @@ export function SaveReportMenu({ }; return ( - <MenuTooltip width={150} onClose={onClose}> - <Menu - onMenuSelect={item => { - onMenuSelect(item); - }} - items={[ - ...savedMenu.items, - ...modifiedMenu.items, - ...unsavedMenu.items, - ]} - /> - </MenuTooltip> + <Menu + onMenuSelect={item => { + onMenuSelect(item); + }} + items={[...savedMenu.items, ...modifiedMenu.items, ...unsavedMenu.items]} + /> ); } diff --git a/packages/desktop-client/src/components/reports/SaveReportName.tsx b/packages/desktop-client/src/components/reports/SaveReportName.tsx index 7357dd029..ee6bd8e51 100644 --- a/packages/desktop-client/src/components/reports/SaveReportName.tsx +++ b/packages/desktop-client/src/components/reports/SaveReportName.tsx @@ -5,14 +5,12 @@ import { type CustomReportEntity } from 'loot-core/types/models/reports'; import { theme } from '../../style'; import { Button } from '../common/Button'; import { Input } from '../common/Input'; -import { MenuTooltip } from '../common/MenuTooltip'; import { Stack } from '../common/Stack'; import { Text } from '../common/Text'; import { View } from '../common/View'; import { FormField, FormLabel } from '../forms'; type SaveReportNameProps = { - onClose: () => void; menuItem: string; name: string; setName: (name: string) => void; @@ -29,7 +27,6 @@ type SaveReportNameProps = { }; export function SaveReportName({ - onClose, menuItem, name, setName, @@ -45,7 +42,7 @@ export function SaveReportName({ }, []); return ( - <MenuTooltip width={325} onClose={onClose}> + <> {menuItem !== 'update-report' && ( <form> <Stack @@ -91,6 +88,6 @@ export function SaveReportName({ ) : ( <View /> )} - </MenuTooltip> + </> ); } diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx index 4e0a4023a..28469b859 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReportListCards.tsx @@ -1,4 +1,4 @@ -import React, { createRef, useEffect, useMemo, useState } from 'react'; +import React, { createRef, useEffect, useMemo, useRef, useState } from 'react'; import { send, sendCatch } from 'loot-core/platform/client/fetch/index'; import * as monthUtils from 'loot-core/src/shared/months'; @@ -14,7 +14,7 @@ import { theme } from '../../../style/theme'; import { Block } from '../../common/Block'; import { Menu } from '../../common/Menu'; import { MenuButton } from '../../common/MenuButton'; -import { MenuTooltip } from '../../common/MenuTooltip'; +import { Popover } from '../../common/Popover'; import { Text } from '../../common/Text'; import { View } from '../../common/View'; import { DateRange } from '../DateRange'; @@ -25,30 +25,27 @@ import { SaveReportName } from '../SaveReportName'; import { GetCardData } from './GetCardData'; type CardMenuProps = { - onClose: () => void; onMenuSelect: (item: string, report: CustomReportEntity) => void; report: CustomReportEntity; }; -function CardMenu({ onClose, onMenuSelect, report }: CardMenuProps) { +function CardMenu({ onMenuSelect, report }: CardMenuProps) { return ( - <MenuTooltip onClose={onClose} width={120}> - <Menu - onMenuSelect={item => { - onMenuSelect(item, report); - }} - items={[ - { - name: 'rename', - text: 'Rename report', - }, - { - name: 'delete', - text: 'Delete report', - }, - ]} - /> - </MenuTooltip> + <Menu + onMenuSelect={item => { + onMenuSelect(item, report); + }} + items={[ + { + name: 'rename', + text: 'Rename report', + }, + { + name: 'delete', + text: 'Delete report', + }, + ]} + /> ); } @@ -72,6 +69,7 @@ export function CustomReportListCards({ const [reportMenu, setReportMenu] = useState(result); const [deleteMenuOpen, setDeleteMenuOpen] = useState(result); const [nameMenuOpen, setNameMenuOpen] = useState(result); + const triggerRef = useRef(null); const [err, setErr] = useState(''); const [name, setName] = useState(''); const inputRef = createRef<HTMLInputElement>(); @@ -257,6 +255,7 @@ export function CustomReportListCards({ }} > <MenuButton + ref={triggerRef} onClick={() => onMenuOpen(report.id === undefined ? '' : report.id, true) } @@ -265,55 +264,80 @@ export function CustomReportListCards({ isCardHovered === report.id ? 'inherit' : 'transparent', }} /> - {report.id === undefined - ? null - : reportMenu[report.id as keyof typeof reportMenu] && ( - <CardMenu - onMenuSelect={onMenuSelect} - onClose={() => - onMenuOpen( - report.id === undefined ? '' : report.id, - false, - ) - } - report={report} - /> - )} - {report.id === undefined - ? null - : nameMenuOpen[report.id as keyof typeof nameMenuOpen] && ( - <SaveReportName - onClose={() => - onNameMenuOpen( - report.id === undefined ? '' : report.id, - false, - ) - } - menuItem="rename" - name={name} - setName={setName} - inputRef={inputRef} - onAddUpdate={onAddUpdate} - err={err} - report={report} - /> - )} - {report.id === undefined - ? null - : deleteMenuOpen[ - report.id as keyof typeof deleteMenuOpen - ] && ( - <SaveReportDelete - onDelete={() => onDelete(report.id)} - onClose={() => - onDeleteMenuOpen( - report.id === undefined ? '' : report.id, - false, - ) - } - name={report.name} - /> - )} + + <Popover + triggerRef={triggerRef} + isOpen={ + !!( + report.id && + reportMenu[report.id as keyof typeof reportMenu] + ) + } + onOpenChange={() => + onMenuOpen( + report.id === undefined ? '' : report.id, + false, + ) + } + style={{ width: 120 }} + > + <CardMenu onMenuSelect={onMenuSelect} report={report} /> + </Popover> + + <Popover + triggerRef={triggerRef} + isOpen={ + !!( + report.id && + nameMenuOpen[report.id as keyof typeof nameMenuOpen] + ) + } + onOpenChange={() => + onNameMenuOpen( + report.id === undefined ? '' : report.id, + false, + ) + } + style={{ width: 325 }} + > + <SaveReportName + menuItem="rename" + name={name} + setName={setName} + inputRef={inputRef} + onAddUpdate={onAddUpdate} + err={err} + report={report} + /> + </Popover> + + <Popover + triggerRef={triggerRef} + isOpen={ + !!( + report.id && + deleteMenuOpen[report.id as keyof typeof deleteMenuOpen] + ) + } + onOpenChange={() => + onDeleteMenuOpen( + report.id === undefined ? '' : report.id, + false, + ) + } + style={{ width: 275, padding: 15 }} + > + <SaveReportDelete + onDelete={() => onDelete(report.id)} + onClose={() => + onDeleteMenuOpen( + report.id === undefined ? '' : report.id, + false, + ) + } + name={report.name} + /> + </Popover> </View> </View> ))} diff --git a/upcoming-release-notes/2778.md b/upcoming-release-notes/2778.md new file mode 100644 index 000000000..fff94737c --- /dev/null +++ b/upcoming-release-notes/2778.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MatissJanis] +--- + +Migrating native `Tooltip` component to react-aria Tooltip/Popover (vol.7) -- GitLab