diff --git a/.eslintrc b/.eslintrc index 2f52429610..f11af1b6e1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,6 +30,7 @@ "jsx-a11y/no-autofocus": "off", "import/no-extraneous-dependencies": "off", "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + "@typescript-eslint/prefer-ts-expect-error": "error", "@typescript-eslint/consistent-type-imports": ["error", {"prefer": "type-imports", "fixStyle": "separate-type-imports"}] }, "overrides": [ diff --git a/CODEOWNERS b/CODEOWNERS index 2dd3d5b98a..c85f4dc1e7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,7 +18,7 @@ /src/components/Hotkey @d3m1d0v /src/components/Icon @amje /src/components/Label @goshander -/src/components/Link @LakeVostok +/src/components/Link @Estasie /src/components/List @korvin89 /src/components/Loader @SeqviriouM /src/components/Menu @NikitaCG diff --git a/src/components/Disclosure/Disclosure.tsx b/src/components/Disclosure/Disclosure.tsx index 1d4a074b62..7353f0513f 100644 --- a/src/components/Disclosure/Disclosure.tsx +++ b/src/components/Disclosure/Disclosure.tsx @@ -42,7 +42,7 @@ export interface DisclosureProps extends QAProps { const isDisclosureSummaryComponent = isOfType(DisclosureSummary); -// @ts-ignore this ts-error is appears when forwarding ref. It complains that DisclosureComposition props is not provided initially +// @ts-expect-error this ts-error is appears when forwarding ref. It complains that DisclosureComposition props is not provided initially export const Disclosure: React.FunctionComponent & DisclosureComposition = React.forwardRef(function Disclosure(props, ref) { const { diff --git a/src/components/Table/__tests__/Table.withTableSettings.test.tsx b/src/components/Table/__tests__/Table.withTableSettings.test.tsx index 1e0f453d9e..9b84eea2b4 100644 --- a/src/components/Table/__tests__/Table.withTableSettings.test.tsx +++ b/src/components/Table/__tests__/Table.withTableSettings.test.tsx @@ -149,10 +149,8 @@ describe('withTableSettings', () => { ); expect(osSettings).toBeDefined(); expect(osxSettings).toBeDefined(); - // @ts-ignore - expect(osSettings.isSelected).toBe(true); - // @ts-ignore - expect(osxSettings.isSelected).toBe(false); + expect(osSettings?.isSelected).toBe(true); + expect(osxSettings?.isSelected).toBe(false); }); it('should return columns when no settings provided', () => { diff --git a/src/components/Table/hoc/withTableActions/withTableActions.tsx b/src/components/Table/hoc/withTableActions/withTableActions.tsx index cdb2f3145b..50e342e056 100644 --- a/src/components/Table/hoc/withTableActions/withTableActions.tsx +++ b/src/components/Table/hoc/withTableActions/withTableActions.tsx @@ -272,7 +272,7 @@ export function withTableActions( return (item: I, index: number, event: React.MouseEvent) => { if ( - // @ts-ignore + // @ts-expect-error event.nativeEvent.target.matches( `.${actionsButtonCn}, .${actionsButtonCn} *`, ) diff --git a/src/components/Table/hoc/withTableCopy/withTableCopy.tsx b/src/components/Table/hoc/withTableCopy/withTableCopy.tsx index 966f009928..b9efad1f2e 100644 --- a/src/components/Table/hoc/withTableCopy/withTableCopy.tsx +++ b/src/components/Table/hoc/withTableCopy/withTableCopy.tsx @@ -104,7 +104,7 @@ export function withTableCopy( return (item: I, index: number, event: React.MouseEvent) => { const buttonClassName = b('copy-button'); if ( - // @ts-ignore + // @ts-expect-error event.nativeEvent.target.matches( `.${buttonClassName}, .${buttonClassName} *`, ) diff --git a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx index cfa4c89b30..497de15a6f 100644 --- a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx +++ b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx @@ -120,7 +120,7 @@ export function withTableSelection( event: React.ChangeEvent, ) => { const {checked} = event.target; - // @ts-ignore shiftKey is defined for click events + // @ts-expect-error shiftKey is defined for click events const isShiftPressed = event.nativeEvent.shiftKey; const {data, selectedIds, onSelectionChange} = this.props; @@ -191,7 +191,7 @@ export function withTableSelection( return (item: I, index: number, event: React.MouseEvent) => { const checkboxClassName = b('selection-checkbox'); if ( - // @ts-ignore + // @ts-expect-error event.nativeEvent.target.matches( `.${checkboxClassName}, .${checkboxClassName} *`, ) diff --git a/src/components/layout/Flex/Flex.tsx b/src/components/layout/Flex/Flex.tsx index 22ca7d3c2c..3a93a16493 100644 --- a/src/components/layout/Flex/Flex.tsx +++ b/src/components/layout/Flex/Flex.tsx @@ -128,7 +128,10 @@ type FlexPropsWithTypedAttrs = FlexProps & * --- * Storybook - https://preview.gravity-ui.com/uikit/?path=/docs/layout--playground#flex */ -export const Flex = function Flex(props: FlexProps) { +export const Flex = React.forwardRef(function Flex( + props: FlexProps, + ref: FlexRef, +) { const { as: propsAs, direction, @@ -187,6 +190,7 @@ export const Flex = function Flex(props: Fl }, className, )} + ref={ref} style={{ flexDirection: applyMediaProps(direction), flexGrow: grow === true ? 1 : grow, @@ -213,6 +217,6 @@ export const Flex = function Flex(props: Fl : children} ); -} as (( +}) as (( props: FlexPropsWithTypedAttrs & {ref?: FlexRef}, ) => React.ReactElement) & {displayName: string}; diff --git a/src/components/layout/demo/ColPresenter/ColPresenter.tsx b/src/components/layout/demo/ColPresenter/ColPresenter.tsx index c203cf7265..d77a5f2bbc 100644 --- a/src/components/layout/demo/ColPresenter/ColPresenter.tsx +++ b/src/components/layout/demo/ColPresenter/ColPresenter.tsx @@ -4,7 +4,7 @@ import {Col} from '../../Col/Col'; import type {ColProps} from '../../Col/Col'; import {Box} from '../Box/Box'; -// @ts-ignore-error +// @ts-expect-error const pickSizeProps = ({l, xl, s, m, xxl, xxxl, size}: T = {}): string => { // skip empty values return Object.entries({...{l, xl, s, m, xxl, xxxl, size}}) diff --git a/src/components/useList/components/ListItemView/ListItemView.tsx b/src/components/useList/components/ListItemView/ListItemView.tsx index aff283be05..49d647a137 100644 --- a/src/components/useList/components/ListItemView/ListItemView.tsx +++ b/src/components/useList/components/ListItemView/ListItemView.tsx @@ -15,11 +15,13 @@ import './ListItemView.scss'; const b = block('list-item-view'); -export interface ListItemViewProps extends QAProps, ListItemCommonProps { +export interface ListItemViewProps + extends QAProps, + ListItemCommonProps { /** * Ability to override default html tag */ - as?: keyof JSX.IntrinsicElements; + as?: T; /** * @default `m` */ @@ -43,7 +45,7 @@ export interface ListItemViewProps extends QAProps, ListItemCommonProps { /** * Note: if passed and `disabled` option is `true` click will not be appear */ - onClick?(): void; + onClick?: React.ComponentPropsWithoutRef['onClick']; style?: React.CSSProperties; className?: string; role?: React.AriaRole; @@ -59,6 +61,11 @@ export interface ListItemViewProps extends QAProps, ListItemCommonProps { id: ListItemId; } +type ListItemViewRef = React.ComponentPropsWithRef['ref']; + +type ListItemViewPropsWithTypedAttrs = ListItemViewProps & + Omit, keyof ListItemViewProps>; + interface SlotProps extends FlexProps { indentation?: number; } @@ -85,118 +92,116 @@ const renderSafeIndentation = (indentation?: number) => { return null; }; -export const ListItemView = React.forwardRef( - ( - { - id, - as = 'div', - size = 'm', - active, - selected, - disabled, - activeOnHover: propsActiveOnHover, - className, - hasSelectionIcon = true, - indentation, - startSlot, - subtitle, - endSlot, - title, - height, - expanded, - dragging, - style, - role = 'option', - onClick: _onClick, - ...rest - }: ListItemViewProps, - ref?: any, - ) => { - const isGroup = typeof expanded === 'boolean'; - const onClick = disabled ? undefined : _onClick; - const activeOnHover = - typeof propsActiveOnHover === 'boolean' ? propsActiveOnHover : Boolean(onClick); +export const ListItemView = React.forwardRef(function ListItemView< + T extends React.ElementType = 'div', +>( + { + id, + as: asProps, + size = 'm', + active, + selected, + disabled, + activeOnHover: propsActiveOnHover, + className, + hasSelectionIcon = true, + indentation, + startSlot, + subtitle, + endSlot, + title, + height, + expanded, + dragging, + style, + role = 'option', + onClick: _onClick, + ...rest + }: ListItemViewPropsWithTypedAttrs, + ref?: ListItemViewRef, +) { + const as: React.ElementType = asProps || 'div'; + const isGroup = typeof expanded === 'boolean'; + const onClick = disabled ? undefined : _onClick; + const activeOnHover = + typeof propsActiveOnHover === 'boolean' ? propsActiveOnHover : Boolean(onClick); - return ( - + + {hasSelectionIcon && ( + + {selected ? ( + + ) : null} + )} - style={{ - minHeight: height ?? modToHeight[size][Number(Boolean(subtitle))], - ...style, - }} - as={as} - ref={ref} - alignItems="center" - gap="4" - justifyContent="space-between" - {...rest} - > - - {hasSelectionIcon && ( - + ) : null} + + {startSlot} + +
+ {typeof title === 'string' ? ( + - {selected ? ( - - ) : null} - + {title} + + ) : ( + title )} - - {renderSafeIndentation(indentation)} - - {isGroup ? ( - - ) : null} - - {startSlot} - -
- {typeof title === 'string' ? ( - - {title} - - ) : ( - title - )} - {typeof subtitle === 'string' ? ( - - {subtitle} - - ) : ( - subtitle - )} -
- - - {endSlot} + {typeof subtitle === 'string' ? ( + + {subtitle} + + ) : ( + subtitle + )} +
- ); - }, -); -ListItemView.displayName = 'ListItemView'; + {endSlot} +
+ ); +}) as ({ + ref, + ...props +}: ListItemViewPropsWithTypedAttrs & {ref?: ListItemViewRef}) => React.ReactElement; diff --git a/test-utils/setup-tests.ts b/test-utils/setup-tests.ts index 2d03f72af0..374470e6e5 100644 --- a/test-utils/setup-tests.ts +++ b/test-utils/setup-tests.ts @@ -20,7 +20,7 @@ global.ResizeObserver = class implements ResizeObserver { jest.mock( 'react-virtualized-auto-sizer', () => - //@ts-ignore + //@ts-expect-error ({children}) => children({height: 400, width: 400}), );