diff --git a/eslint.config.mjs b/eslint.config.mjs index 201eeeda8..946b0f7a8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -49,7 +49,6 @@ export default tseslint.config( 'jest.config.ts', 'notices.template.html', 'tools', - 'typings', 'vite.config.mts', ], extends: [ diff --git a/src/Frontend/Components/Autocomplete/Listbox/Listbox.tsx b/src/Frontend/Components/Autocomplete/Listbox/Listbox.tsx index 276667302..6dfaf3246 100644 --- a/src/Frontend/Components/Autocomplete/Listbox/Listbox.tsx +++ b/src/Frontend/Components/Autocomplete/Listbox/Listbox.tsx @@ -12,7 +12,7 @@ import { } from '@mui/material/useAutocomplete'; import { SxProps } from '@mui/system'; import { groupBy as _groupBy } from 'lodash'; -import { forwardRef, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; import { GroupedVirtuoso, Virtuoso, VirtuosoHandle } from 'react-virtuoso'; import { GroupContainer, styles } from './Listbox.style'; @@ -21,6 +21,7 @@ export type ListboxProps< Value, FreeSolo extends boolean | undefined, > = React.HTMLAttributes & { + ref?: React.RefObject; virtuosoRef: React.RefObject; options: Array; groupProps?: { @@ -61,147 +62,134 @@ interface Groups { firstSelectedIndex: number; } -export const Listbox = forwardRef( - ( - { - virtuosoRef, - closePopper, - getOptionKey, - getOptionProps, - groupBy, - groupProps, - optionText, - options, - renderOptionEndIcon, - renderOptionStartIcon, - ...listboxProps - }: ListboxProps, - forwardedRef: React.ForwardedRef, - ) => { - const [height, setHeight] = useState(Number.MAX_SAFE_INTEGER); // will result in max-height +export const Listbox = ({ + virtuosoRef, + closePopper, + getOptionKey, + getOptionProps, + groupBy, + groupProps, + optionText, + options, + renderOptionEndIcon, + renderOptionStartIcon, + ref, + ...listboxProps +}: ListboxProps) => { + const [height, setHeight] = useState(Number.MAX_SAFE_INTEGER); // will result in max-height - const groups = useMemo((): Groups | undefined => { - if (!groupBy) { - return undefined; - } + const groups = useMemo((): Groups | undefined => { + if (!groupBy) { + return undefined; + } - const grouped = _groupBy(options, groupBy); - const flattened = Object.values(grouped).flat(); + const grouped = _groupBy(options, groupBy); + const flattened = Object.values(grouped).flat(); - return { - options: flattened, - groupNames: Object.keys(grouped), - groupCounts: Object.values(grouped).map((group) => group.length), - firstSelectedIndex: flattened.findIndex( - (option, index) => - !!getOptionProps({ option, index })['aria-selected'], - ), - }; - }, [getOptionProps, groupBy, options]); + return { + options: flattened, + groupNames: Object.keys(grouped), + groupCounts: Object.values(grouped).map((group) => group.length), + firstSelectedIndex: flattened.findIndex( + (option, index) => !!getOptionProps({ option, index })['aria-selected'], + ), + }; + }, [getOptionProps, groupBy, options]); - return ( - - {groups ? renderGroupedList(groups) : renderList(options)} - - ); + return ( + + {groups ? renderGroupedList(groups) : renderList(options)} + + ); - function renderGroupedList({ - groupCounts, - groupNames, - options, - firstSelectedIndex, - }: Groups) { - return ( - ) { + return ( + { - const IconComp = groupProps?.icon || (() => null); - const ActionComp = groupProps?.action || (() => null); + } + totalListHeightChanged={setHeight} + groupCounts={groupCounts} + groupContent={(index) => { + const IconComp = groupProps?.icon || (() => null); + const ActionComp = groupProps?.action || (() => null); - return ( - - - - {groupNames[index]} - - - - ); - }} - itemContent={(index) => - renderOption({ index, option: options[index] }) - } - /> - ); - } + return ( + + + + {groupNames[index]} + + + + ); + }} + itemContent={(index) => renderOption({ index, option: options[index] })} + /> + ); + } - function renderList(options: Array) { - return ( - renderOption({ option, index })} - increaseViewportBy={20} - totalListHeightChanged={setHeight} - /> - ); - } + function renderList(options: Array) { + return ( + renderOption({ option, index })} + increaseViewportBy={20} + totalListHeightChanged={setHeight} + /> + ); + } - function renderOption({ - index, + function renderOption({ + index, + option, + }: UseAutocompleteRenderedOption) { + const { key, ...optionProps } = getOptionProps({ option, - }: UseAutocompleteRenderedOption) { - const { key, ...optionProps } = getOptionProps({ - option, - index, - }); + index, + }); - return ( - - {renderOptionStartIcon?.(option, { closePopper })} - - {renderOptionEndIcon?.(option, { closePopper })} - - ); - } - }, -); + return ( + + {renderOptionStartIcon?.(option, { closePopper })} + + {renderOptionEndIcon?.(option, { closePopper })} + + ); + } +}; diff --git a/src/Frontend/Components/Checkbox/Checkbox.tsx b/src/Frontend/Components/Checkbox/Checkbox.tsx index 38b331cad..8cfb7b0a8 100644 --- a/src/Frontend/Components/Checkbox/Checkbox.tsx +++ b/src/Frontend/Components/Checkbox/Checkbox.tsx @@ -8,7 +8,6 @@ import { SxProps, } from '@mui/material'; import MuiCheckbox from '@mui/material/Checkbox'; -import { forwardRef } from 'react'; interface CheckboxProps extends Pick { checked: boolean; @@ -17,51 +16,48 @@ interface CheckboxProps extends Pick { indeterminate?: boolean; label?: string; onChange(event: React.ChangeEvent): void; + ref?: React.RefObject; sx?: SxProps; } -export const Checkbox = forwardRef( - ( - { - checked, - disableRipple, - disabled, - indeterminate, - label, - labelPlacement, - onChange, - sx, - ...props - }, - ref, - ) => { - return ( - - } - /> - ); - }, -); +export const Checkbox: React.FC = ({ + checked, + disableRipple, + disabled, + indeterminate, + label, + labelPlacement, + onChange, + ref, + sx, + ...props +}) => { + return ( + + } + /> + ); +}; diff --git a/src/Frontend/Components/ConfirmationDialog/ConfirmationDialog.tsx b/src/Frontend/Components/ConfirmationDialog/ConfirmationDialog.tsx index 684b3b34d..c8cb1b92b 100644 --- a/src/Frontend/Components/ConfirmationDialog/ConfirmationDialog.tsx +++ b/src/Frontend/Components/ConfirmationDialog/ConfirmationDialog.tsx @@ -2,13 +2,7 @@ // SPDX-FileCopyrightText: TNG Technology Consulting GmbH // // SPDX-License-Identifier: Apache-2.0 -import { - forwardRef, - useCallback, - useImperativeHandle, - useRef, - useState, -} from 'react'; +import { useCallback, useImperativeHandle, useRef, useState } from 'react'; import { text } from '../../../shared/text'; import { NotificationPopup } from '../NotificationPopup/NotificationPopup'; @@ -62,6 +56,7 @@ export function useConfirmationDialog( } export interface ConfirmationDialogProps { + ref: React.RefObject; title: string; message: React.ReactNode; } @@ -79,55 +74,57 @@ export interface ConfirmationDialogProps { * // ... * */ -export const ConfirmationDialog = forwardRef( - ({ message, title }, ref) => { - const [open, setOpen] = useState(false); - const resolveRef = useRef<(value: boolean) => void>(undefined); - - useImperativeHandle( - ref, - () => - async (onConfirm, { onCancel, skip } = {}) => { - if (skip) { - void onConfirm?.(); - return true; - } - - setOpen(true); - - const result = await new Promise((resolve) => { - resolveRef.current = resolve; - }); - - if (result) { - void onConfirm?.(); - } else { - void onCancel?.(); - } - - setOpen(false); - - return result; - }, - [], - ); - - return ( - resolveRef.current?.(false), - }} - centerLeftButtonConfig={{ - buttonText: text.buttons.ok, - onClick: () => resolveRef.current?.(true), - }} - aria-label={'confirmation dialog'} - /> - ); - }, -); +export const ConfirmationDialog: React.FC = ({ + ref, + message, + title, +}) => { + const [open, setOpen] = useState(false); + const resolveRef = useRef<(value: boolean) => void>(undefined); + + useImperativeHandle( + ref, + () => + async (onConfirm, { onCancel, skip } = {}) => { + if (skip) { + void onConfirm?.(); + return true; + } + + setOpen(true); + + const result = await new Promise((resolve) => { + resolveRef.current = resolve; + }); + + if (result) { + void onConfirm?.(); + } else { + void onCancel?.(); + } + + setOpen(false); + + return result; + }, + [], + ); + + return ( + resolveRef.current?.(false), + }} + centerLeftButtonConfig={{ + buttonText: text.buttons.ok, + onClick: () => resolveRef.current?.(true), + }} + aria-label={'confirmation dialog'} + /> + ); +}; diff --git a/src/Frontend/Components/GroupedList/GroupedList.tsx b/src/Frontend/Components/GroupedList/GroupedList.tsx index 98cf151ad..6221ecb2c 100644 --- a/src/Frontend/Components/GroupedList/GroupedList.tsx +++ b/src/Frontend/Components/GroupedList/GroupedList.tsx @@ -98,9 +98,7 @@ export function GroupedList({ {loading && } {groups && ( // Virtuoso components must not be inlined: https://github.com/petyosi/react-virtuoso/issues/566 - + setIsVirtuosoFocused(true)} @@ -133,7 +131,7 @@ export function GroupedList({ } {...props} /> - + )} ); diff --git a/src/Frontend/Components/List/List.tsx b/src/Frontend/Components/List/List.tsx index 316bcb13e..d7f17fdbe 100644 --- a/src/Frontend/Components/List/List.tsx +++ b/src/Frontend/Components/List/List.tsx @@ -63,9 +63,7 @@ export function List({ {loading && } {data && ( // Virtuoso components must not be inlined: https://github.com/petyosi/react-virtuoso/issues/566 - + setIsVirtuosoFocused(true)} @@ -86,7 +84,7 @@ export function List({ } {...props} /> - + )} ); diff --git a/src/Frontend/Components/ReportView/TableConfig.tsx b/src/Frontend/Components/ReportView/TableConfig.tsx index ec940a2f1..fb67a5dd4 100644 --- a/src/Frontend/Components/ReportView/TableConfig.tsx +++ b/src/Frontend/Components/ReportView/TableConfig.tsx @@ -7,7 +7,6 @@ import TableBody from '@mui/material/TableBody'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; -import { forwardRef } from 'react'; import { TableComponents } from 'react-virtuoso'; import { PackageInfo } from '../../../shared/shared-types'; @@ -86,7 +85,7 @@ export const tableConfigs: Array = [ // Virtuoso components must not be inlined: https://github.com/petyosi/react-virtuoso/issues/566 export const TABLE_COMPONENTS: TableComponents = { - Scroller: forwardRef((props, ref) => ), + Scroller: (props) => , Table: (props) => ( = { style={{ borderCollapse: 'separate' }} /> ), - TableHead: forwardRef((props, ref) => ), + TableHead: (props) => , TableRow: (props) => { const selectedAttributionId = useAppSelector(getSelectedAttributionId); @@ -107,5 +106,5 @@ export const TABLE_COMPONENTS: TableComponents = { /> ); }, - TableBody: forwardRef((props, ref) => ), + TableBody: (props) => , }; diff --git a/src/Frontend/Components/ResizableBox/ResizableBox.tsx b/src/Frontend/Components/ResizableBox/ResizableBox.tsx index e1ad74a95..b0b3287c2 100644 --- a/src/Frontend/Components/ResizableBox/ResizableBox.tsx +++ b/src/Frontend/Components/ResizableBox/ResizableBox.tsx @@ -4,41 +4,45 @@ // SPDX-License-Identifier: Apache-2.0 import { SxProps } from '@mui/system'; import { Resizable, ResizableProps } from 're-resizable'; -import { forwardRef } from 'react'; interface Props extends Omit { children: React.ReactNode; + ref?: React.RefObject; sx?: SxProps; } -export const ResizableBox = forwardRef( - ({ children, enable, sx, ...props }, ref) => { - return ( - - {children} - - ); - }, -); +export const ResizableBox: React.FC = ({ + children, + enable, + ref, + sx, + ...props +}) => { + return ( + + {children} + + ); +}; diff --git a/src/Frontend/Components/ResizePanels/ResizePanels.tsx b/src/Frontend/Components/ResizePanels/ResizePanels.tsx index e442f60bc..5195a4401 100644 --- a/src/Frontend/Components/ResizePanels/ResizePanels.tsx +++ b/src/Frontend/Components/ResizePanels/ResizePanels.tsx @@ -148,9 +148,9 @@ export const ResizePanels: React.FC = ({ showSearch: !isUpperCollapsed, searchRef: upperSearchRef, })} - + {upperPanel.component} - + ); } @@ -187,9 +187,9 @@ export const ResizePanels: React.FC = ({ showSearch: !isLowerCollapsed, searchRef: lowerSearchRef, })} - + {lowerPanel.component} - + ); } diff --git a/src/Frontend/Components/SearchList/SearchList.tsx b/src/Frontend/Components/SearchList/SearchList.tsx index b11c3e335..3006d85f2 100644 --- a/src/Frontend/Components/SearchList/SearchList.tsx +++ b/src/Frontend/Components/SearchList/SearchList.tsx @@ -2,14 +2,13 @@ // SPDX-FileCopyrightText: TNG Technology Consulting GmbH // // SPDX-License-Identifier: Apache-2.0 -import { forwardRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { ListProps } from 'react-virtuoso'; import { useSearchRef } from '../SearchRefContext/SearchRefContext'; import { useVirtuosoComponent } from '../VirtuosoComponentContext/VirtuosoComponentContext'; -export const SearchList: React.FC = forwardRef((props, ref) => { +export const SearchList: React.FC = (props) => { const searchRef = useSearchRef(); const { isVirtuosoFocused } = useVirtuosoComponent(); useHotkeys( @@ -21,5 +20,5 @@ export const SearchList: React.FC = forwardRef((props, ref) => { [isVirtuosoFocused], ); - return
; -}); + return
; +}; diff --git a/src/Frontend/test-helpers/render.tsx b/src/Frontend/test-helpers/render.tsx index 79bb7b1b7..3006c7e16 100644 --- a/src/Frontend/test-helpers/render.tsx +++ b/src/Frontend/test-helpers/render.tsx @@ -36,11 +36,11 @@ export function renderComponent( wrapper: ({ children }) => ( - {children} - + ), diff --git a/tsconfig.json b/tsconfig.json index 4708d4778..49b031e72 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,6 @@ "@testing-library/jest-dom" ] }, - "include": ["src/**/*", "typings"], + "include": ["src/**/*"], "exclude": ["src/ElectronBackend/**/*"] } diff --git a/typings/react.d.ts b/typings/react.d.ts deleted file mode 100644 index 2dd988532..000000000 --- a/typings/react.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates -// SPDX-FileCopyrightText: TNG Technology Consulting GmbH -// -// SPDX-License-Identifier: Apache-2.0 -import { ReactNode, Ref, RefAttributes } from 'react'; - -declare module 'react' { - // Overwrite type definition of forwardRef to allow forwarding generic components. - // See https://fettblog.eu/typescript-react-generic-forward-refs/ - function forwardRef( - render: (props: P, ref: Ref) => React.ReactElement | null, - ): (props: P & RefAttributes) => React.ReactElement | null; -}