Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"eslint-plugin-compat": "^6.0.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.2.0",
"eslint-plugin-jsdoc": "^50.6.8",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-compiler": "0.0.0-experimental-56229e1-20240813",
Expand Down
31 changes: 30 additions & 1 deletion packages/vkui/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if (!E2E_TEST || !E2E_PLAYGROUND_HELPERS) {
module.exports = {
root: false,
extends: ['plugin:react-hooks/recommended', 'plugin:react-server-components/recommended'],
plugins: ['import', '@project-tools/vkui', 'unicorn'],
plugins: ['import', '@project-tools/vkui', 'unicorn', 'eslint-plugin-jsdoc'],
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
Expand Down Expand Up @@ -136,8 +136,37 @@ module.exports = {
},
],
'unicorn/expiring-todo-comments': ['error'],
Comment thread
andrey-medvedev-vk marked this conversation as resolved.
'jsdoc/require-jsdoc': 'off',
'jsdoc/check-syntax': 'off',
},
overrides: [
{
files: ['src/components/**/*.{ts,tsx}'],
excludedFiles: ['src/components/**/*.{test,e2e,e2e-playground,stories}.{ts,tsx}'],
rules: {
'jsdoc/require-description-complete-sentence': ['error'],
'jsdoc/require-jsdoc': [
'error',
{
contexts: [
'TSTypeAliasDeclaration > TSIntersectionType > TSTypeLiteral > TSPropertySignature',
'TSTypeAliasDeclaration > TSTypeLiteral > TSPropertySignature',
'TSInterfaceDeclaration TSPropertySignature:not(TSTypeLiteral TSPropertySignature)',
],
require: {
FunctionDeclaration: false,
FunctionExpression: false,
ArrowFunctionExpression: false,
MethodDefinition: false,
},
checkConstructors: false,
checkGetters: false,
checkSetters: false,
enableFixer: false,
},
],
},
},
{
files: ['src/**/*.{ts,tsx}'],
excludedFiles: [
Expand Down
6 changes: 6 additions & 0 deletions packages/vkui/src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ function useAccordionId(id: AccordionProps['id']) {
}

export interface AccordionProps extends HasChildren {
/**
* Используется для генерации id для заголовка и контента(a11y).
*/
id?: string;
/**
* Управляет раскрытием и скрытием контента.
Expand All @@ -29,6 +32,9 @@ export interface AccordionProps extends HasChildren {
* Возвращает новое значение при изменении раскрытия/сворачивания контента.
*/
onChange?: (newValue: boolean) => void;
/**
* Блокировка взаимодействия с компонентом.
*/
disabled?: boolean;
}

Expand Down
13 changes: 13 additions & 0 deletions packages/vkui/src/components/Accordion/AccordionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,22 @@ import * as React from 'react';
import { noop } from '@vkontakte/vkjs';

export interface AccordionContextProps {
/**
* [a11y] Id для `Accordion.Summary`.
*/
labelId: string;
/**
* [a11y] Id для свойства `Accordion.Content`.
*/
contentId: string;
/**
* Состояние аккордеона
* `true` - аккордеон развернут, `false` - аккордеон свернут.
*/
expanded: boolean;
/**
* Обработчик изменения состояния аккордеона.
*/
onChange: (e: boolean) => void;
}

Expand Down
17 changes: 16 additions & 1 deletion packages/vkui/src/components/ActionSheet/ActionSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import styles from './ActionSheet.module.css';

type CloseInitiators = 'action-item' | 'cancel-item' | 'other';
export interface ActionSheetOnCloseOptions {
/**
* Причина закрытия всплывающего элемента.
*/
closedBy: CloseInitiators;
}

Expand All @@ -29,16 +32,28 @@ export interface ActionSheetProps
>,
Omit<UseFocusTrapProps, 'onClose'>,
Omit<React.HTMLAttributes<HTMLDivElement>, 'autoFocus' | 'title'> {
/**
* Заголовок всплыващего окна.
*/
title?: React.ReactNode;
/**
* Описание всплыващего окна, под заголовком.
*/
description?: React.ReactNode;
/**
* Закрыть попап по клику снаружи.
* Закрыть всплыващее окно по нажатию снаружи.
*/
onClose: (options: ActionSheetOnCloseOptions) => void;
/**
* Только мобильный iOS.
*/
iosCloseItem?: React.ReactNode;
/**
* Режим отображения компонента:
*
* - `sheet` – отображение снизу экрана в виде всплывающего окна, подходит для мобильных устройств
* - `menu` – отображение в виде всплывающего элемента, относительно якорного элемента.
*/
mode?: 'sheet' | 'menu';
}

Expand Down
23 changes: 23 additions & 0 deletions packages/vkui/src/components/ActionSheet/ActionSheetContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,38 @@ import * as React from 'react';
export type ActionType<T> = (event: React.MouseEvent<T>) => void;

export type ItemClickHandler<T extends Element = Element> = (options: {
/**
* Действие, которое будет выполненно после завершения анимации скрытия и после вызова onClose.
* Из этого следует, что в объекте события значения полей типа `currentTarget` будут не определены.
* Если вам нужен объект события именно на момент нажатия, используйте `immediateAction`.
*/
action: ActionType<T> | undefined;
/**
* Действие, которое выполняется непосредственно в момент нажатия (в отличие от `action`).
*/
immediateAction: ActionType<T> | undefined;
/**
* Флаг, указывающий, нужно ли автоматически закрывать `ActionSheet`.
*/
autoClose: boolean;
/**
* Флаг, указывающий является ли элемент кнопкой отмены.
*/
isCancelItem: boolean;
}) => (event: React.MouseEvent) => void;

export type ActionSheetContextType<T extends Element = Element> = {
/**
* Обработчик нажатия на элемент.
*/
onItemClick?: ItemClickHandler<T>;
/**
* Обработчик закрытия `ActionSheet`.
*/
onClose?: () => void;
/**
* Режим отображения `ActionSheet`.
*/
mode?: 'sheet' | 'menu';
};

Expand Down
13 changes: 8 additions & 5 deletions packages/vkui/src/components/ActionSheet/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@ import { type FocusTrapProps } from '../FocusTrap/FocusTrap';
export type ToggleRef = Element | null | undefined | React.RefObject<Element | null>;

export interface SharedDropdownProps extends FocusTrapProps {
/**
* Состояние закрытия всплывающего окна.
*/
closing: boolean;
/**
* Элемент, рядом с которым вылезает попап на десктопе.
* Лучше передавать RefObject c current.
* Элемент, рядом с которым вылезает всплывающий элемент на десктопе.
* Лучше передавать `RefObject` c current.
*/
toggleRef: ToggleRef;
/**
* Позиционирование всплывающего окна для десктопа.
* Компонент выберет наилучшее расположение сам, но можно задать приоритетное направление с помощью этого свойства
* Компонент выберет наилучшее расположение сам, но можно задать приоритетное направление с помощью этого свойства.
*/
placement?: PlacementWithAuto;
/**
* Отступ, где заданное кол-во единиц равняется пикселям
* Отступ, где заданное кол-во единиц равняется пикселям.
* */
popupOffsetDistance?: number;
/**
* По умолчанию событие onClick не всплывает
* По умолчанию событие `onClick` не всплывает.
*/
allowClickPropagation?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,68 @@ export interface ActionSheetItemProps
extends React.HTMLAttributes<HTMLElement>,
React.AnchorHTMLAttributes<HTMLElement>,
Pick<React.InputHTMLAttributes<HTMLInputElement>, 'name' | 'checked' | 'value'> {
/**
* Свойство, определяющее внешний вид элемента действия.
*/
mode?: 'default' | 'destructive' | 'cancel';
/**
* Если указано, элемент будет использоваться как ссылка.
*/
href?: string;
/**
* Атрибут `target` для тега `<a>`.
*/
target?: string;
/**
* Иконка или другой React-элемент для отображения перед основным контентом.
*/
before?: React.ReactNode;
/**
* Иконка или другой React-элемент для отображения после основного контента.
*/
after?: React.ReactNode;
/**
* Дополнительная информация, отображаемая рядом с основным контентом.
*/
meta?: React.ReactNode;
/**
* Слот для подсказки или вспомогательного текста.
*/
subtitle?: React.ReactNode;
/**
* По умолчанию клик на опцию вызывает переданную в `ActionSheet` функцию `onClose`, данное свойство
* позволяет отключить такое поведение
* По умолчанию нажатие на опцию вызывает переданную в `ActionSheet` функцию `onClose`, данное свойство
* позволяет отключить такое поведение.
*/
autoCloseDisabled?: boolean;
/**
* Включает возможность выбрать элемент (отображает радиокнопку).
*/
selectable?: boolean;
/**
* Блокировка взаимодействия с компонентом.
*/
disabled?: boolean;
/**
* Все текстовые элементы при необходимости занимают несколько строк
* Все текстовые элементы при необходимости занимают несколько строк.
*/
multiline?: boolean;
/**
* По умолчанию onClick будет вызван после завершения анимации скрытия и после вызова onClose.
* По умолчанию `onClick` будет вызван после завершения анимации скрытия и после вызова `onClose`.
* Из этого следует, что в объекте события значения полей типа `currentTarget` будут не определены.
* Если вам нужен объект события именно на момент клика, используйте `onImmediateClick`.
* Если вам нужен объект события именно на момент нажатия, используйте `onImmediateClick`.
*/
onClick?: React.MouseEventHandler<HTMLElement>;
/**
* Обработчик нажатия, вызывающийся непосредственно в момент нажатия (в отличие от `onClick`).
*/
onImmediateClick?: React.MouseEventHandler<HTMLElement>;
/**
* Иконка для `checked` режима.
*/
iconChecked?: React.ReactNode;
/**
* Позволяет отделить ActionItem от CancelItem для определении того,
* кто вызвал закрытие ActionSheet. Используется в ActionSheet.onClose()
* Позволяет отделить `ActionItem` от `CancelItem` для определении того,
* кто вызвал закрытие `ActionSheet`. Используется в `ActionSheet.onClose()`.
*/
isCancelItem?: boolean;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/vkui/src/components/ActionSheetItem/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
* По дизайну `ActionSheet` должен закрывать при клике на `ActionSheetItem`.
* По дизайну `ActionSheet` должен закрывать при нажатии на `ActionSheetItem`.
* В режиме `selectable` в реализации используются нативный input type=radio
* И при навигации стрелочками по элементам происходит событие `click` из-за чего `ActionSheet` закрывается.
* Поэтому нужно как-то отличить реальное событие клика
* Поэтому нужно как-то отличить реальное событие нажатия.
* @see https://github.com/facebook/react/issues/7407
* @see https://github.com/VKCOM/VKUI/issues/6954
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import * as React from 'react';
import { useAdaptivityConditionalRender } from '../../hooks/useAdaptivityConditionalRender';

export interface AdaptiveIconRendererProps {
/**
* Компонент иконки для компактного размера.
*/
IconCompact: React.ComponentType<{ className?: string }>;
/**
* Компонент иконки для обычного размера.
*/
IconRegular: React.ComponentType<{ className?: string }>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ import * as React from 'react';
import type { SizeTypeValues, ViewHeightType, ViewWidthType } from '../../lib/adaptivity';

export interface SizeProps {
/**
* Тип размера экрана по горизонтали.
*/
sizeX?: SizeTypeValues;
/**
* Тип размера экрана по вертикали.
*/
sizeY?: SizeTypeValues;
}

export interface AdaptivityProps extends SizeProps {
/**
* @ignore
* Тип ширины экрана для адаптивного отображения.
*/
viewWidth?: ViewWidthType;
/**
* @ignore
* Тип высоты экрана для адаптивного отображения.
*/
viewHeight?: ViewHeightType;
/**
* @ignore
* Флаг наличия указателя (например, мыши) на устройстве.
*/
hasPointer?: boolean;
/**
* @ignore
* Флаг поддержки эффекта наведения на устройстве.
*/
hasHover?: boolean;
}
Expand Down
Loading
Loading