diff --git a/.babelrc b/.babelrc index b5cf683b7e..8ebad08fb0 100644 --- a/.babelrc +++ b/.babelrc @@ -10,7 +10,12 @@ } ], "@babel/preset-typescript", - "@babel/preset-react" + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ] ], "plugins": [] -} \ No newline at end of file +} diff --git a/.eslintrc b/.eslintrc index b38843c8a8..7df42133dc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,7 +4,8 @@ "@gravity-ui/eslint-config/client", "@gravity-ui/eslint-config/prettier", "@gravity-ui/eslint-config/import-order", - "@gravity-ui/eslint-config/a11y" + "@gravity-ui/eslint-config/a11y", + "plugin:react/jsx-runtime" ], "root": true, "rules": { @@ -15,8 +16,8 @@ "no-restricted-syntax": [ "error", { - "selector": "ImportDeclaration[source.value='react'] :matches(ImportNamespaceSpecifier, ImportSpecifier)", - "message": "Please use import React from 'react' instead." + "selector": "ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportSpecifier)", + "message": "Please use `import * as React from 'react'` instead." }, { "selector": "TSTypeReference>TSQualifiedName[left.name='React'][right.name='FC']", diff --git a/.storybook/decorators/withLang.tsx b/.storybook/decorators/withLang.tsx index 9d7d513a5e..544c8cd8f6 100644 --- a/.storybook/decorators/withLang.tsx +++ b/.storybook/decorators/withLang.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Decorator} from '@storybook/react'; import {configure} from '../../src'; diff --git a/.storybook/decorators/withMobile.tsx b/.storybook/decorators/withMobile.tsx index b8d05b8f5a..b6c104ad2a 100644 --- a/.storybook/decorators/withMobile.tsx +++ b/.storybook/decorators/withMobile.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Decorator} from '@storybook/react'; import {MobileProvider} from '../../src'; diff --git a/.storybook/decorators/withStrictMode.tsx b/.storybook/decorators/withStrictMode.tsx index 9858f782f9..b3d9c1cd2a 100644 --- a/.storybook/decorators/withStrictMode.tsx +++ b/.storybook/decorators/withStrictMode.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Decorator} from '@storybook/react'; diff --git a/.storybook/decorators/withTheme.tsx b/.storybook/decorators/withTheme.tsx index 0f73ea4150..ba64e71a49 100644 --- a/.storybook/decorators/withTheme.tsx +++ b/.storybook/decorators/withTheme.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Decorator} from '@storybook/react'; import {ThemeProvider} from '../../src'; diff --git a/.storybook/theme-addon/register.tsx b/.storybook/theme-addon/register.tsx index 30061a0c8d..a24e92bd57 100644 --- a/.storybook/theme-addon/register.tsx +++ b/.storybook/theme-addon/register.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {API} from '@storybook/manager-api'; import {addons, types, useGlobals} from '@storybook/manager-api'; diff --git a/CHANGELOG.md b/CHANGELOG.md index c8545d1fb0..b288cec496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # Changelog +## [6.40.0](https://github.com/gravity-ui/uikit/compare/v6.39.0...v6.40.0) (2024-12-24) + + +### Features + +* **Select:** add data for option group ([#2011](https://github.com/gravity-ui/uikit/issues/2011)) ([46c9d28](https://github.com/gravity-ui/uikit/commit/46c9d28b972ad63dd9ce334b1d052b76fa5720ec)) + +## [6.39.0](https://github.com/gravity-ui/uikit/compare/v6.38.0...v6.39.0) (2024-12-19) + + +### Features + +* **Progress:** change prop text ([#1993](https://github.com/gravity-ui/uikit/issues/1993)) ([92e53a0](https://github.com/gravity-ui/uikit/commit/92e53a088112d41a1fe96f091350f36d0caaa97c)) +* **Select:** add title for option ([#1994](https://github.com/gravity-ui/uikit/issues/1994)) ([03b5dbf](https://github.com/gravity-ui/uikit/commit/03b5dbfbe68abf98de3407643140ddb226379d51)) +* **styles:** manage animations by user settings ([#1996](https://github.com/gravity-ui/uikit/issues/1996)) ([19ca365](https://github.com/gravity-ui/uikit/commit/19ca365acde4fb5b467e9ac36a373a6ea5def728)) +* **Tabs:** allow string for TabItem counter value ([#1989](https://github.com/gravity-ui/uikit/issues/1989)) ([b628086](https://github.com/gravity-ui/uikit/commit/b6280863bb474f9d52fdc1f3f26dd5e0507254df)) + + +### Bug Fixes + +* **Icon:** remove redundant color:inherit style ([#1999](https://github.com/gravity-ui/uikit/issues/1999)) ([d6cda6e](https://github.com/gravity-ui/uikit/commit/d6cda6ebf38216da1eac56e3b87343603cf29c63)) +* **Sheet:** remove animation lags when closing in some browsers ([#1984](https://github.com/gravity-ui/uikit/issues/1984)) ([555b186](https://github.com/gravity-ui/uikit/commit/555b18687ce431fe78102b0e98fac35464fe7957)) + +## [6.38.0](https://github.com/gravity-ui/uikit/compare/v6.37.0...v6.38.0) (2024-12-06) + + +### Features + +* **User:** add html titles for elements ([#1982](https://github.com/gravity-ui/uikit/issues/1982)) ([105d43b](https://github.com/gravity-ui/uikit/commit/105d43b0d511e516675e66877fb31c94bf583f91)) + + +### Bug Fixes + +* **PinInput:** remove height glitch in Safari ([#1938](https://github.com/gravity-ui/uikit/issues/1938)) ([d6cb1cf](https://github.com/gravity-ui/uikit/commit/d6cb1cfaec89a806b6948f92aa2c12669c110fb8)) +* **TextInput:** share font styles between error and note blocks in OuterAdditionalContent ([#1970](https://github.com/gravity-ui/uikit/issues/1970)) ([55737b5](https://github.com/gravity-ui/uikit/commit/55737b5a66dbd975ecbbf84dac95592cd65e3b1a)) + ## [6.37.0](https://github.com/gravity-ui/uikit/compare/v6.36.0...v6.37.0) (2024-11-27) diff --git a/CODEOWNERS b/CODEOWNERS index dd3fceac5c..21f1bbd245 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,6 +18,7 @@ /src/components/Disclosure @Raubzeug /src/components/Divider @v4dyar4 /src/components/DropdownMenu @axtk +/src/components/FilePreview @mournfulCoroner /src/components/HelpMark @Raubzeug /src/components/Hotkey @d3m1d0v /src/components/Icon @amje diff --git a/README-ru.md b/README-ru.md new file mode 100644 index 0000000000..692f14243e --- /dev/null +++ b/README-ru.md @@ -0,0 +1,107 @@ +# UIKit · [![npm package](https://img.shields.io/npm/v/@gravity-ui/uikit)](https://www.npmjs.com/package/@gravity-ui/uikit) [![CI](https://img.shields.io/github/actions/workflow/status/gravity-ui/uikit/.github/workflows/ci.yml?branch=main&label=CI&logo=github)](https://github.com/gravity-ui/uikit/actions/workflows/ci.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/uikit/) + +Набор гибких, универсальных и высокоэффективных React-компонентов для создания сложных веб-приложений. + + + +![Cover image](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/uikit_cover.png) + +## Ресурсы + +### ![Globe Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/globe_light.svg#gh-light-mode-only) ![Globe Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/globe_dark.svg#gh-dark-mode-only) [Веб-сайт](https://gravity-ui.com) + +### ![Documentation Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/book-open_light.svg#gh-light-mode-only) ![Documentation Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/book-open_dark.svg#gh-dark-mode-only) [Документация](https://gravity-ui.com/components/uikit/alert) + +### ![Figma Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/figma_light.svg#gh-light-mode-only) ![Figma Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/figma_dark.svg#gh-dark-mode-only) [Figma]() + +### ![Themer Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/bucket-paint_light.svg#gh-light-mode-only) ![Themer Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/bucket-paint_dark.svg#gh-dark-mode-only) [Themer](https://gravity-ui.com/themer) + +### ![Storybook Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/storybook_light.svg#gh-light-mode-only) ![Storybook Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/storybook_dark.svg#gh-dark-mode-only) [Storybook](https://preview.gravity-ui.com/uikit/) + +### ![Community Logo Light](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/telegram_light.svg#gh-light-mode-only) ![Community Logo Dark](https://raw.githubusercontent.com/gravity-ui/uikit/main/docs/assets/telegram_dark.svg#gh-dark-mode-only) [Сообщество](https://t.me/gravity_ui) + + + +## Установка + +```shell +npm install --save-dev @gravity-ui/uikit +``` + +## Использование + +```jsx +import React from 'react'; +import {Button} from '@gravity-ui/uikit'; + +const SubmitButton = +); +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :----------------------------------------------------- | :------: | :-------------------: | +| className | HTML-атрибут `class`. | `string` | | +| direction | Задает направление `arrowToggle`. | `string` | `"bottom"` | +| size | Размер `arrowToggle` (в пикселях). | `number` | `16` | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | diff --git a/src/components/ArrowToggle/README.md b/src/components/ArrowToggle/README.md index 91356d54e7..ae68ce8f2f 100644 --- a/src/components/ArrowToggle/README.md +++ b/src/components/ArrowToggle/README.md @@ -4,11 +4,11 @@ -`ArrowToggle` is a component for displaying the chevron icon. It can rotate in four directions, and can be used to display dropdown lists, cut components, etc. +`ArrowToggle` is a component for displaying the chevron icon. It can rotate in four directions and can be used to display drop-down lists, cut components, etc. ## Appearance -The component has four possible directions: `top`, `right`, `bottom` and `left`. +`ArrowToggle` has four possible directions: `top`, `right`, `bottom`, and `left`. -## Interactive +## Use as an interactive element -Here is an example of usage ArrowToggle component with a toggling icon. +Here is an example of using ArrowToggle with a toggling icon: + +# Avatar + + + +```tsx +import {Avatar} from '@gravity-ui/uikit'; +``` + +Данный компонент предназначен для рендеринга аватаров. Он поддерживает три основных типа аватаров: изображение, иконку и текст (инициалы). Все эти типы имеют специальные свойства для настройки их поведения и внешнего вида. + +## Типы + +### Изображение + +Компонент `Avatar` можно применять для рендеринга аватаров с использованием изображений. Для добавления изображения используйте свойство `imgUrl`. + + + +Дополнительно можно передать свойство `srcSet`, которое позволяет загружать изображения разных размеров. + + + +У компонента `Avatar` есть свойство `fallbackImgUrl`, которое позволяет передать изображение, которое будет показано при ошибке загрузки изображения по ссылке `imgUrl` (ошибка CSP или отсутствие исходного изображения). + + + +### Иконка + +Этот компонент можно применять для рендеринга аватаров с использованием иконок. Чтобы задать иконку, используйте свойство `icon` точно так же, как для компонента `Icon`. + + + +### Текст + +Этот компонент можно применять для рендеринга аватаров с использованием текста. Для этого используйте свойство `text`. Текст отображается в виде инициалов (первых букв двух слов) или первых двух букв одного слова. + + + +## Внешний вид + +### Тема и вид + +Компонент `Avatar` имеет предустановленные темы (`normal` и `brand`) и виды (`filled` и `outlined`). + +По умолчанию тема — `normal`, а вид — `filled`. + + + +### Пользовательские цвета + +Можно также задать собственные цвета через свойства `backgroundColor`, `borderColor` и `color` (последнее работает только для аватарок с иконками и текстом). Эти цвета обладают бóльшим приоритетом по сравнению с цветами, которые заданы в темах. + + + +### Размер + +Размер `Avatar` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. Возможные значения: `xs`, `s`, `m`, `l` и `xl`. + + + +## Свойства + +### Общие + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------------- | :----------------------------------------------------- | :-----------------------------: | :-------------------: | +| size | Размер аватара. | `'xs'` `'s'` `'m'` `'l'` `'xl'` | `m` | +| theme | Тема аватара. | `'normal'` `'brand'` | `normal` | +| view | Варианты заполнения и обводки аватара. | `'filled'` `'outlined'` | `filled` | +| backgroundColor | Пользовательский цвет фона. | `string` | | +| borderColor | Пользовательский цвет границы. | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| aria-label | Атрибут `aria-label` для секции аватара. | `string` | | +| aria-labelledby | Атрибут `aria-labelledby` для секции аватара. | `string` | | +| className | Пользовательский CSS-класс корневого элемента. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | + +### Свойства изображений + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------- | :--------------------------------------------------- | :----------------: | :-------------------: | +| imgUrl | HTML-атрибут `src` для `img`. | `string` | | +| fallbackImgUrl | Резервное изображение, отображаемое в случае ошибки. | `string` | | +| sizes | HTML-атрибут `sizes` для `img`. | `string` | | +| srcSet | HTML-атрибут `srcSet` для `img`. | `string` | | +| alt | HTML-атрибут `alt` для `img`. | `string` | props.title | +| loading | HTML-атрибут `loading` для `img`. | `'eager'` `'lazy'` | | + +### Свойства иконки + +| Имя | Описание | Тип | Значение по умолчанию | +| :---- | :---------------------------- | :--------: | :-------------------: | +| icon | Источник SVG-иконки. | `IconData` | | +| color | Пользовательский цвет иконки. | `string` | | + +### Свойства текста + +| Имя | Описание | Тип | Значение по умолчанию | +| :---- | :------------------------------ | :------: | :-------------------: | +| text | Текст, отображаемый на аватаре. | `string` | | +| color | Пользовательский цвет текста. | `string` | | + +## API CSS + +| Имя | Описание | +| :---------------------------- | :------------------------ | +| `--g-avatar-size` | Размер (ширина и высота). | +| `--g-avatar-background-color` | Цвет фона. | +| `--g-avatar-border-color` | Цвет границы. | +| `--g-avatar-color` | Цвет иконки и текста. | +| `--g-avatar-font-size` | Размер шрифта текста. | +| `--g-avatar-line-height` | Высота строки текста. | diff --git a/src/components/Avatar/README.md b/src/components/Avatar/README.md index 63f2edbcce..fa7fc961e3 100644 --- a/src/components/Avatar/README.md +++ b/src/components/Avatar/README.md @@ -8,13 +8,13 @@ import {Avatar} from '@gravity-ui/uikit'; ``` -The component intended to render avatars. It has three basic types of avatars: image, icon and text (initials). All of these types have special props to configure behaviour and appearance. +This component is intended for rendering avatars. It has three basic avatar types: image, icon, and text (name initials). All these types have special properties to configure the behavior and appearance. ## Types ### Image -This component can be used to render avatars using images. Provide the image via `imgUrl` property. +This component can be used to render avatars using images. To provide an image, use the `imgUrl` property. -Also, you can provide `srcSet` property to load images of different sizes. +You can also provide the `srcSet` property to load images of different sizes. -Avatar component has `fallbackImgUrl` property which allows you to provide the image that is shown when an image loading error occurs via the link `imgUrl` (CSP error or no original image). +The `Avatar` component has the `fallbackImgUrl` property which allows you to provide the image that is shown when an image loading error occurs, through the `imgUrl` link (CSP error or no original image). ### Icon -This component can be used to render avatars using icons. Provide the icon via `icon` property like in `Icon` component. +This component can be used to render avatars using icons. Use the `icon` property to provide an icon, just like you would do in case of the `Icon` component. ### Text -This component can be used to render avatars using text. Provide the text via `text` property. The text renders like initials (2 first letters of words) or just 2 first letters of a single word. +This component can be used to render avatars using text. Use the `text` property for that. The text is rendered as initials (first letters of two words) or just two first letters of a single word. ### Theme and view -The Avatar component has predefined themes (`normal`, `brand`) and views (`filled`, `outlined`) +The `Avatar` component has predefined themes (`normal`, `brand`) and views (`filled`, `outlined`). -Default theme: `normal` -Default view: `filled` +The default theme is `normal` and the default view is `filled`. ### Custom colors -Also, you can provide custom colors via props `backgroundColor`, `borderColor` and `color` (works only for icon and text avatars). These colors have a higher priority than the colors from the theme. +You can also provide custom colors through the `backgroundColor`, `borderColor`, and `color` properties (the latter works only for icon and text avatars). These colors have a higher priority than the theme colors. ### Size -To control the size of the `Avatar` use the `size` property. The default size is `m`. Possible values: `2xs`, `xs`, `s`, `m`, `l`, `xl`. +Use the `size` property to manage the `Avatar` size. The default size is `m`. The possible values are `xs`, `s`, `m`, `l`, and `xl`. ### Common -| Name | Description | Type | Default | -| :-------------- | :-------------------------------------- | :-------------------------------------: | :------: | -| size | Avatar size | `'2xs'` `'xs'` `'s'` `'m'` `'l'` `'xl'` | `m` | -| theme | Avatar theme | `'normal'` `'brand'` | `normal` | -| view | Avatar view | `'filled'` `'outlined'` | `filled` | -| backgroundColor | Custom background color | `string` | | -| borderColor | Custom border color | `string` | | -| title | HTML `title` attributes | `string` | | -| aria-label | `aria-label` for avatar block | `string` | | -| aria-labelledby | `aria-labelledby` for avatar block | `string` | | -| className | Custom CSS class for root element | `string` | | -| style | HTML style attribute | `React.CSSProperties` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :-------------- | :----------------------------------------- | :-----------------------------: | :------: | +| size | Avatar size | `'xs'` `'s'` `'m'` `'l'` `'xl'` | `m` | +| theme | Avatar theme | `'normal'` `'brand'` | `normal` | +| view | Avatar filling and outlining options | `'filled'` `'outlined'` | `filled` | +| backgroundColor | Custom background color | `string` | | +| borderColor | Custom border color | `string` | | +| title | `title` HTML attribute | `string` | | +| aria-label | `aria-label` for the avatar section | `string` | | +| aria-labelledby | `aria-labelledby` for the avatar section | `string` | | +| className | Custom CSS class for the root element | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | ### Image-specific -| Name | Description | Type | Default | -| :------------- | :-------------------------------------- | :----------------: | :---------: | -| imgUrl | HTML img `src` attribute | `string` | | -| fallbackImgUrl | Fallback image, shown if error happened | `string` | | -| sizes | HTML img `sizes` attribute | `string` | | -| srcSet | HTML img `srcSet` attribute | `string` | | -| alt | HTML img `alt` attribute | `string` | props.title | -| loading | HTML img `loading` attribute | `'eager'` `'lazy'` | | +| Name | Description | Type | Default | +| :------------- | :---------------------------------------- | :----------------: | :---------: | +| imgUrl | `img` `src` HTML attribute | `string` | | +| fallbackImgUrl | Fallback image shown if an error occurred | `string` | | +| sizes | `img` `sizes` HTML attribute | `string` | | +| srcSet | `img` `srcSet` HTML attribute | `string` | | +| alt | `img` `alt` HTML attribute | `string` | props.title | +| loading | `img` `loading` HTML attribute | `'eager'` `'lazy'` | | ### Icon-specific diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-chromium-linux.png index d849f90c6b..6c921c86f5 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-webkit-linux.png deleted file mode 100644 index 0c343c9af8..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Icon-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-chromium-linux.png index 8a7a8b192f..431c60db6e 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-webkit-linux.png deleted file mode 100644 index 603f4ccf56..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Image-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-ImageFallback-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-ImageFallback-1-chromium-linux.png index 8a7a8b192f..431c60db6e 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-ImageFallback-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-ImageFallback-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-chromium-linux.png index e8d1040607..2465ea9ab7 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-webkit-linux.png deleted file mode 100644 index fa62a36eea..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Showcase-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-chromium-linux.png index e662f2dc70..2dc3864f4b 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-webkit-linux.png deleted file mode 100644 index 15f6aba578..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-Text-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-chromium-linux.png index e8015e0799..8cc43ca1eb 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-webkit-linux.png deleted file mode 100644 index c36ea7324d..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-TextInitials-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-chromium-linux.png index d279870575..423b5227f6 100644 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-chromium-linux.png and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-webkit-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-webkit-linux.png deleted file mode 100644 index f7f0b7ce61..0000000000 Binary files a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-render-story-WithBorder-1-webkit-linux.png and /dev/null differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-icon-light-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-icon-light-chromium-linux.png new file mode 100644 index 0000000000..8b9e1fcd9e Binary files /dev/null and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-icon-light-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-image-light-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-image-light-chromium-linux.png new file mode 100644 index 0000000000..7a08d6926a Binary files /dev/null and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-image-light-chromium-linux.png differ diff --git a/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-text-light-chromium-linux.png b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-text-light-chromium-linux.png new file mode 100644 index 0000000000..4da4f7a938 Binary files /dev/null and b/src/components/Avatar/__snapshots__/Avatar.visual.test.tsx-snapshots/Avatar-smoke-with-text-light-chromium-linux.png differ diff --git a/src/components/Avatar/__stories__/Avatar.stories.tsx b/src/components/Avatar/__stories__/Avatar.stories.tsx index 9cf2207c4d..0c2ff961d0 100644 --- a/src/components/Avatar/__stories__/Avatar.stories.tsx +++ b/src/components/Avatar/__stories__/Avatar.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {faker} from '@faker-js/faker/locale/en'; import {FaceRobot} from '@gravity-ui/icons'; diff --git a/src/components/Avatar/__tests__/Avatar.visual.test.tsx b/src/components/Avatar/__tests__/Avatar.visual.test.tsx index 32899fd3b0..da9000f7f3 100644 --- a/src/components/Avatar/__tests__/Avatar.visual.test.tsx +++ b/src/components/Avatar/__tests__/Avatar.visual.test.tsx @@ -1,12 +1,23 @@ -import React from 'react'; - import {expect} from '@playwright/experimental-ct-react'; -import {test} from '~playwright/core'; - +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import {Avatar} from '../Avatar'; +import type {AvatarProps} from '../types/main'; + +import { + backgroundColorCases, + borderColorCases, + sizeCases, + themeCases, + titleCases, + viewCases, +} from './cases'; +import {TestAvatarWithIcon, TestAvatarWithImage} from './helpersPlaywright'; import {AvatarStories} from './stories'; -test.describe('Avatar', () => { +test.describe('Avatar', {tag: '@Avatar'}, () => { test('render story: ', async ({mount}) => { const component = await mount(); @@ -50,4 +61,106 @@ test.describe('Avatar', () => { await expect(component).toHaveScreenshot(); }); + + const defaultProps: AvatarProps = {}; + + const commonCases = { + size: sizeCases, + theme: themeCases, + view: viewCases, + backgroundColor: backgroundColorCases, + borderColor: borderColorCases, + title: titleCases, + } as const; + + smokeTest('with image', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + defaultProps, + { + ...commonCases, + }, + { + scenarioName: 'image specific', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('with icon', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + defaultProps, + { + ...commonCases, + }, + { + scenarioName: 'icon specific', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('with text', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + text: 'Text', + color: 'black', + }, + { + ...commonCases, + }, + { + scenarioName: 'text specific', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); }); diff --git a/src/components/Avatar/__tests__/cases.tsx b/src/components/Avatar/__tests__/cases.tsx new file mode 100644 index 0000000000..a5d0ff527d --- /dev/null +++ b/src/components/Avatar/__tests__/cases.tsx @@ -0,0 +1,9 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {AvatarProps} from '../types/main'; + +export const sizeCases: Cases = ['xs', 's', 'm', 'l', 'xl']; +export const themeCases: Cases = ['normal', 'brand']; +export const viewCases: Cases = ['filled', 'outlined']; +export const backgroundColorCases: Cases = ['darkblue']; +export const borderColorCases: Cases = ['tomato']; +export const titleCases: Cases = ['Title']; diff --git a/src/components/Avatar/__tests__/helpersPlaywright.tsx b/src/components/Avatar/__tests__/helpersPlaywright.tsx new file mode 100644 index 0000000000..f98838aa53 --- /dev/null +++ b/src/components/Avatar/__tests__/helpersPlaywright.tsx @@ -0,0 +1,19 @@ +import {FaceRobot} from '@gravity-ui/icons'; + +import {Avatar} from '../Avatar'; +import type {AvatarProps} from '../types/main'; + +export const TestAvatarWithImage = (props: AvatarProps) => { + return ( + + ); +}; + +export const TestAvatarWithIcon = (props: AvatarProps) => { + return ; +}; diff --git a/src/components/AvatarStack/AvatarStack.tsx b/src/components/AvatarStack/AvatarStack.tsx index 378fad7430..9578e5c914 100644 --- a/src/components/AvatarStack/AvatarStack.tsx +++ b/src/components/AvatarStack/AvatarStack.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {block} from '../utils/cn'; diff --git a/src/components/AvatarStack/AvatarStackItem.tsx b/src/components/AvatarStack/AvatarStackItem.tsx index b21aaa415f..37ca62a698 100644 --- a/src/components/AvatarStack/AvatarStackItem.tsx +++ b/src/components/AvatarStack/AvatarStackItem.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import {block} from '../utils/cn'; diff --git a/src/components/AvatarStack/AvatarStackMore.tsx b/src/components/AvatarStack/AvatarStackMore.tsx index 1ee09fcf91..9365531c97 100644 --- a/src/components/AvatarStack/AvatarStackMore.tsx +++ b/src/components/AvatarStack/AvatarStackMore.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {DEFAULT_AVATAR_SIZE} from '../Avatar'; import {block} from '../utils/cn'; diff --git a/src/components/AvatarStack/AvatarStackMoreButton.tsx b/src/components/AvatarStack/AvatarStackMoreButton.tsx index 2793f2bdd4..616bd5fade 100644 --- a/src/components/AvatarStack/AvatarStackMoreButton.tsx +++ b/src/components/AvatarStack/AvatarStackMoreButton.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {DEFAULT_AVATAR_SIZE} from '../Avatar'; import {block} from '../utils/cn'; diff --git a/src/components/AvatarStack/README-ru.md b/src/components/AvatarStack/README-ru.md new file mode 100644 index 0000000000..e203b1d174 --- /dev/null +++ b/src/components/AvatarStack/README-ru.md @@ -0,0 +1,52 @@ + + +# AvatarStack + + + +```ts +import {AvatarStack} from '@gravity-ui/uikit'; +``` + +Этот компонент служит для создания стека изображений с наложением друг на друга. Опционально также может быть предусмотрен контрол. Обычно относится к аватарам пользователей. + +## Использование + +В целом `AvatarStack` не накладывает никаких ограничений на то, какие компоненты можно рендерить. Ниже приведен пример его типичного использования: + +```tsx + + + + + +``` + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-------------------: | +| max | Устанавливает максимальное количество аватаров, которые будут отображаться перед кнопкой `More`. Если количество аватаров меньше `max` всего на 1, кнопка `More` будет заменена на аватар. | `number` | 3 | +| overlapSize | Определяет, насколько каждый элемент должен перекрывать следующий. Для аватаров размером от `xs` до `m` рекомендуется использовать `s`, для `l` — `m`, а для `xl` — `l`. | `s`, `m`, `l` | `s` | +| size | Размер контрола, отображающего дополнительные аватары. Значение соответствует размеру `Avatar`. | `AvatarSize` | | +| className | Имя CSS-класса корневого DOM-узла. | `string` | | +| children | Список аватаров, которые могут иметь дополнительные обертки. | `Object[]` | | +| renderMore | Пользовательская функция для отрисовки контрола, отображающего дополнительные аватары. | `function(options: {count: number}): ReactElement` | | + +### AvatarStack.MoreButton + +Компонент для переопределения кнопки `More`. + +```tsx + ( + + + + )} +> + + + + +``` diff --git a/src/components/AvatarStack/README.md b/src/components/AvatarStack/README.md index 70ac7141df..d1efb5bc09 100644 --- a/src/components/AvatarStack/README.md +++ b/src/components/AvatarStack/README.md @@ -8,11 +8,11 @@ import {AvatarStack} from '@gravity-ui/uikit'; ``` -Stack of images with overlap over next image and optional control. This is usually users avatars. +This component is used for a stack of images with overlap over one another and, optionally, a control. It usually refers to user avatars. ## Usage -Component is not limit you to what components to render, basic usage is: +Basically, `AvatarStack` does not have any limitations in terms of what components to render. See an example of its common usage below: ```tsx @@ -24,19 +24,18 @@ Component is not limit you to what components to render, basic usage is: ## Properties -| Name | Description | Type | Default | -| :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-----: | -| max | How much avatars should be visible before more button. If avatars count is only 1 short from `max`, than more button would be replaced with avatar. | `number` | 3 | -| total | Total amount of items, used to calculate number of not rendered avatars | `number` | | -| overlapSize | How much each item should overlap next one. `s` recommended for `Avatar`'s of sizes `xs`-`m`, `m` recomended for `l` size avatars and `l` overlap for `xl` avatars | `s`, `m`, `l` | `s` | -| size | Size for control displaying extra avatars. Value same to `Avatar` size. | `AvatarSize` | | -| className | Class name of root DOM node | `string` | | -| children | List of avatars, probably with some extra wrappers | `Object[]` | | -| renderMore | Custom render for control displaying extra avatars | `function(options: {count: number}): ReactElement` | | +| Name | Description | Type | Default | +| :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-----: | +| max | Determines how many avatars are visible before the `More` button. If avatar count is only 1 short from `max`, the `More` button will be replaced with an avatar. | `number` | 3 | +| overlapSize | Determines how much each item should overlap the next one. You may want to use `s` for `xs` to `m`-sized `Avatar`s , `m` for `l`-sized, and `l`, for `xl`-sized. | `s`, `m`, `l` | `s` | +| size | Size for the control displaying extra avatars. Its value is the same as the `Avatar` size. | `AvatarSize` | | +| className | Class name of the root DOM node | `string` | | +| children | List of avatars that may also have extra wrappers | `Object[]` | | +| renderMore | Custom render for the control displaying extra avatars | `function(options: {count: number}): ReactElement` | | ### AvatarStack.MoreButton -Component for overriding more button +Component for overriding the `More` button ```tsx ``` - -Alternatively, `` could be used. This component doesn't have ` + + +``` + + + +### Контурная кнопка (`outlined`) + +`outlined` — используется для вторичных действий, требующих меньшего внимания. Может использоваться как с основной кнопкой, так и без нее; при этом, если есть основная кнопка, она должна быть акцентирована. + +`outlined-action`: Обычно используется как ссылка на другую страницу или внешний ресурс. + +Этот тип кнопки также имеет дополнительные семантические варианты: `outlined-info`, `outlined-success`, `outlined-warning` и `outlined-danger`. + + + + + +```tsx + + + + + + + +``` + + + +### Плоская кнопка (`flat`) + +`flat` — используется для вспомогательных действий, требующих наименьшего внимания. Такие элементы часто встречаются в списках кнопок или иконок действий (без текста) в редакторах. + +`flat-secondary` — менее акцентированная версия кнопки `flat`. Часто используется в качестве вспомогательной кнопки в диалогах и модальных окнах. + +`flat-action` — обычно используется как ссылка на другую страницу или внешний ресурс. + +Также имеет дополнительные семантические варианты: `outlined-info`, `outlined-success`, `outlined-warning` и `outlined-danger`. + + + + + +```tsx + + + + + + + + +``` + + + +### Контрастная кнопка (`contrast`) + +Кнопки `normal-contrast`, `outline-contrast` и `flat-contrast` выделяют действия на фоне сложного фона, например, на баннере или фоне с инверсией. + + + + + +```tsx + + + +``` + + + +## Иконки + +Чтобы добавить иконку в `Button`, используйте компонент [`Icon`](../Icon), который представляет собой обертку для SVG-файлов. + + + + + +```tsx + + + + +``` + + + +## Состояния + +`Button` может иметь разные состояния: + +`disabled` — когда взаимодействие с кнопкой по каким-либо причинам недоступно. + +`loading` — когда в фоновом режиме выполняются асинхронные процессы. + +`selected` — когда пользователь может может включить (**Enable**) или отключить (**Disable**) кнопку. + + + + + +```tsx + + + +``` + + + +## Размер + +Размер `Button` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx + + + + + +``` + + + +## Ширина + +Для управления поведением компонента `Button` внутри контейнера используйте свойство `width`: + +`auto` — ограничивает максимальную ширину кнопки, скрывая переполняющее содержимое с помощью многоточия. + +`max` — подгоняет ширину кнопки под размер родительского контейнера, также скрывая переполняющее содержимое с помощью многоточия. + + + +## Форматирование краев + +Свойство `pin` позволяет настраивать форму _начальных_ и _конечных_ краев элемента и обычно используется для объединения нескольких кнопок в единый блок. +Значение свойства `pin` включает названия стилей _начального_ и _конечного_ краев, разделенных дефисом, например, `round-brick`. +Доступные стили краев: `round` (по умолчанию), `circle`, `brick` и `clear`. + + + + + +```tsx +
+ + +
+
+ + + +
+
+ + + + +
+``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :----------- | :-------------------------------------------------------------------------- | :-----------------------------: | :-------------------: | +| children | Содержимое кнопки. Можно использовать как текст, так и компонент ``. | `ReactNode` | | +| className | HTML-атрибут `class`. | `string` | | +| component | Переопределяет корневой компонент. | `ElementType` | `"button"` | +| disabled | Включает или отключает состояние `disabled`. | `false` | `false` | +| extraProps | Дополнительные свойства. | `Record` | | +| href | HTML-атрибут `href`. | `string` | | +| id | HTML-атрибут `id`. | `string` | | +| loading | Включает или отключает состояние `loading`. | `false` | `false` | +| onBlur | Обработчик события `blur`. | `Function` | | +| onClick | Обработчик события `click`. | `Function` | | +| onFocus | Обработчик события `focus`. | `Function` | | +| onMouseEnter | Обработчик события `mouseenter`. | `Function` | | +| onMouseLeave | Обработчик события `mouseleave`. | `Function` | | +| pin | Задает стиль краев кнопки. | `string` | `"round-round"` | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| rel | HTML-атрибут `rel`. | `string` | | +| selected | Включает или отключает состояние `selected`. | | | +| size | Задает размер кнопки. | `string` | `"m"` | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| tabIndex | HTML-атрибут `tabIndex`. | `number` | | +| target | HTML-атрибут `target`. | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| type | HTML-атрибут `type`. | `"button"` `"submit"` `"reset"` | `"button"` | +| view | Задает внешний вид кнопки. | `string` | `"normal"` | +| width | `"auto"` `"max"` | `"auto"` `"max"` | | + +## API CSS + +| Имя | Описание | +| :---------------------------------- | :------------------------------------- | +| `--g-button-text-color` | Цвет текста. | +| `--g-button-text-color-hover` | Цвет текста при ховере. | +| `--g-button-background-color` | Цвет фона. | +| `--g-button-background-color-hover` | Цвет фона при ховере. | +| `--g-button-border-width` | Ширина границы. | +| `--g-button-border-color` | Цвет границы. | +| `--g-button-border-style` | Стиль границы. | +| `--g-button-focus-outline-width` | Толщина контура при получении фокуса. | +| `--g-button-focus-outline-color` | Цвет контура при получении фокуса. | +| `--g-button-focus-outline-style` | Стиль контура при получении фокуса. | +| `--g-button-focus-outline-offset` | Смещение контура при получении фокуса. | +| `--g-button-height` | Высота (высота строки). | +| `--g-button-padding` | Боковые отступы. | +| `--g-button-border-radius` | Радиус скругления углов. | +| `--g-button-font-size` | Размер шрифта текста. | +| `--g-button-icon-size` | Размер иконки. | +| `--g-button-icon-offset` | Смещение иконки. | diff --git a/src/components/Button/README.md b/src/components/Button/README.md index 7b854c711a..0c86e93e33 100644 --- a/src/components/Button/README.md +++ b/src/components/Button/README.md @@ -8,23 +8,21 @@ import {Button} from '@gravity-ui/uikit'; ``` -Buttons act as a trigger for certain actions. While this is their main purpose, in very rare cases, -they can be used as links to navigate to another pages. +Buttons act as a trigger for certain actions. While this is their main purpose, in some very rare cases, they can be used as links to navigate to other pages. ## Appearance -There are four `Button` view types: basic, outlined, flat and contrast. -The `Button`'s appearance is determined by the `view` property. +There are four `Button` types in terms of appearance: basic, outlined, flat, and contrast. +The `Button` appearance is determined by the `view` property. ### Basic -`action` - the most prominent button, used for the primary action on a screen which requires the most attention. +`action`: The most distinctive type of `Button`. It is used for the primary action on a screen that requires the most attention. We recommend using only one such button per page. -`normal` - default type of the `Button`, designed for secondary actions or to maintain the importance of an -action without drawing too much attention to it. +`normal`: Default type of `Button` designed for secondary actions or to maintain the importance of an action without drawing too much attention to it. -`raised` - placed above the content as a "floating" element, usually with a fixed location. +`raised`: Placed above the content as a floating element, usually with a fixed location. ### Outlined -`outlined` - used for secondary actions that require less attention on a page. Can be used with or without a main button (only with an accented one). +`outlined`: Used for secondary actions that require less attention. It can be used with or without a main button; in the former case, it must be an emphasized one. -`outlined-action` - usually used as a link to another page or external resource. +`outlined-action`: Usually used as a link to another page or external resource. -There are also semantic variants of this type, which can be used when additional semantics are needed: `outlined-info`, `outlined-success`, `outlined-warning`, `outlined-danger`. +This type also has semantic variations that can be used when additional semantics are needed: `outlined-info`, `outlined-success`, `outlined-warning`, and `outlined-danger`. ### Flat -`flat` - used for auxiliary actions that require the least attention on a page. It is often used in a list of buttons or action icons (with no text) in an editor. +`flat`: Used for auxiliary actions that require the least attention. It is often used in a list of buttons or action icons (without text) in an editor. -`flat-secondary` - less accented than the `flat` button. It's often used as the secondary button in dialog boxes and modal windows. +`flat-secondary`: Less emphasized than the `flat` button. It is often used as a secondary button in dialog boxes and modal windows. -`flat-action` - usually used as link to another page or external resource. +`flat-action`: Usually used as a link to another page or external resource. -There are also semantic variants of this view, which can be used in places where additional semantic needed: `flat-info`, `flat-success`, `flat-warning`, `flat-danger`. +It also has semantic variations that can be used where additional semantics are needed: `outlined-info`, `outlined-success`, `outlined-warning`, and `outlined-danger`. ### Contrast -`normal-contrast`, `outline-contrast` and `flat-contrast` buttons highlight actions against complex background, e.g., in a banner or against an inverse background. +The `normal-contrast`, `outline-contrast`, and `flat-contrast` buttons highlight actions against complex background, e.g., in a banner, or against an inverse background. ## Icons -To add an icon to the `Button`, you should use the [`Icon`](../Icon) component, a special wrapper for SVGs. +To add an icon to a `Button`, use the [`Icon`](../Icon) component, which is a special wrapper for SVGs. ## States -The `Button` can be in different states. +A `Button` can have different states: -`disabled` - when the button is unavailable for some reason. +`disabled`: When the button is unavailable for some reason. -`loading` - when some asynchronous processes are happening in the background, `selected` - when the user can switch between "Enable" and "Disable". +`loading`: When some asynchronous processes are running in the background. + +`selected`: When the user can **Enable** and **Disable** the button. ## Size -To control the size of the `Button` use the `size` property. Default size is `m`. +Use the `size` property to manage the `Button` size. The default size is `m`. ## Width -The `width` property controls how the `Button` behaves inside the container. +Use the `width` property to manage the way the `Button` behaves inside the container: -`auto` - limits the maximum width of the component, hides overflowing content with an ellipsis. +`auto`: Limits the maximum width of the `Button` by hiding the overflowing content with an ellipsis. -`max` - matches the width to the width of the parent container, also hides overflowing content with an ellipsis. +`max`: Matches the `Button` width to the width of the parent container, also hiding the overflowing content with an ellipsis. ## Pin -The `pin` property allows you to control the shape of the "start" and "end" edges and is usually used for combining multiple buttons in a single unit. -The value of the `pin` property consists of "start" and "end" style names divided by a dash, e.g. `"round-brick"`. -The edge styles are: `round` (default), `circle`, `brick` and `clear`. +The `pin` property allows you to manage the shape of the _start_ and _end_ edges and is usually used for combining multiple buttons in a single unit. +The `pin` property value consists of the _start_ and _end_ style names separated by a hyphen, e.g., `round-brick`. +The edge styles are: `round` (default), `circle`, `brick`, and `clear`. ## Properties -| Name | Description | Type | Default | -| :----------- | :-------------------------------------------------------- | :-----------------------------: | :-------------: | -| children | Button content. You can mix text with `` component | `ReactNode` | | -| className | HTML `class` attribute | `string` | | -| component | Overrides the root component | `ElementType` | `"button"` | -| disabled | Toggles `disabled` state | `false` | `false` | -| extraProps | Any additional props | `Record` | | -| href | HTML `href` attribute | `string` | | -| id | HTML `id` attribute | `string` | | -| loading | Toggles `loading` state | `false` | `false` | -| onBlur | `blur` event handler | `Function` | | -| onClick | `click` event handler | `Function` | | -| onFocus | `focus` event handler | `Function` | | -| onMouseEnter | `mouseenter` event handler | `Function` | | -| onMouseLeave | `mouseleave` event handler | `Function` | | -| pin | Sets button edges style | `string` | `"round-round"` | -| qa | HTML `data-qa` attribute, used in tests | `string` | | -| rel | HTML `rel` attribute | `string` | | -| selected | Toggles `selected` state | | | -| size | Sets button size | `string` | `"m"` | -| style | HTML `style` attribute | `React.CSSProperties` | | -| tabIndex | HTML `tabIndex` attribute | `number` | | -| target | HTML `target` attribute | `string` | | -| title | HTML `title` attribute | `string` | | -| type | HTML `type` attribute | `"button"` `"submit"` `"reset"` | `"button"` | -| view | Sets button appearance | `string` | `"normal"` | -| width | `"auto"` `"max"` | `"auto"` `"max"` | | +| Name | Description | Type | Default | +| :----------- | :----------------------------------------------------------------- | :-----------------------------: | :-------------: | +| children | Button content. You can use both text and the `` component. | `ReactNode` | | +| className | `class` HTML attribute | `string` | | +| component | Overrides the root component | `ElementType` | `"button"` | +| disabled | Toggles the `disabled` state | `false` | `false` | +| extraProps | Additional properties | `Record` | | +| href | `href` HTML attribute | `string` | | +| id | `id` HTML attribute | `string` | | +| loading | Toggles the `loading` state | `false` | `false` | +| onBlur | `blur` event handler | `Function` | | +| onClick | `click` event handler | `Function` | | +| onFocus | `focus` event handler | `Function` | | +| onMouseEnter | `mouseenter` event handler | `Function` | | +| onMouseLeave | `mouseleave` event handler | `Function` | | +| pin | Sets the button edge style | `string` | `"round-round"` | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| rel | `rel` HTML attribute | `string` | | +| selected | Toggles the `selected` state | | | +| size | Sets the button size | `string` | `"m"` | +| style | `style` HTML attribute | `React.CSSProperties` | | +| tabIndex | `tabIndex` HTML attribute | `number` | | +| target | `target` HTML attribute | `string` | | +| title | `title` HTML attribute | `string` | | +| type | `type` HTML attribute | `"button"` `"submit"` `"reset"` | `"button"` | +| view | Sets the button appearance | `string` | `"normal"` | +| width | `"auto"` `"max"` | `"auto"` `"max"` | | ## CSS API @@ -487,11 +487,11 @@ LANDING_BLOCK--> | `--g-button-border-width` | Border width | | `--g-button-border-color` | Border color | | `--g-button-border-style` | Border style | -| `--g-button-focus-outline-width` | Focus outline color | +| `--g-button-focus-outline-width` | Focus outline width | | `--g-button-focus-outline-color` | Focus outline color | | `--g-button-focus-outline-style` | Focus outline style | | `--g-button-focus-outline-offset` | Focus outline offset | -| `--g-button-height` | Height, line-height | +| `--g-button-height` | Height (line height) | | `--g-button-padding` | Side paddings | | `--g-button-border-radius` | Border radius | | `--g-button-font-size` | Text font size | diff --git a/src/components/Button/__stories__/Button.stories.tsx b/src/components/Button/__stories__/Button.stories.tsx index ecd4c57206..b485a93c86 100644 --- a/src/components/Button/__stories__/Button.stories.tsx +++ b/src/components/Button/__stories__/Button.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import { ArrowUpRightFromSquare, diff --git a/src/components/Button/__stories__/ButtonViewShowcase.tsx b/src/components/Button/__stories__/ButtonViewShowcase.tsx index 244c340efa..e8aaafa511 100644 --- a/src/components/Button/__stories__/ButtonViewShowcase.tsx +++ b/src/components/Button/__stories__/ButtonViewShowcase.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Gear} from '@gravity-ui/icons'; import {Icon} from '../../Icon'; diff --git a/src/components/Button/__tests__/Button.test.tsx b/src/components/Button/__tests__/Button.test.tsx index a2f4e4537a..ec28285ad1 100644 --- a/src/components/Button/__tests__/Button.test.tsx +++ b/src/components/Button/__tests__/Button.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Gear} from '@gravity-ui/icons'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Button/__tests__/Button.visual.test.tsx b/src/components/Button/__tests__/Button.visual.test.tsx index 7780557817..ec3203c2fc 100644 --- a/src/components/Button/__tests__/Button.visual.test.tsx +++ b/src/components/Button/__tests__/Button.visual.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {smokeTest, test} from '~playwright/core'; import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; diff --git a/src/components/Button/__tests__/helpersPlaywright.tsx b/src/components/Button/__tests__/helpersPlaywright.tsx index c22d3c6769..c69ce4edcd 100644 --- a/src/components/Button/__tests__/helpersPlaywright.tsx +++ b/src/components/Button/__tests__/helpersPlaywright.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {ChevronDown, Globe} from '@gravity-ui/icons'; import {composeStories} from '@storybook/react'; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 1221007337..2ae313fe20 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useActionHandlers} from '../../hooks'; import {Box} from '../layout'; diff --git a/src/components/Card/README-ru.md b/src/components/Card/README-ru.md new file mode 100644 index 0000000000..f76f5d0aa1 --- /dev/null +++ b/src/components/Card/README-ru.md @@ -0,0 +1,157 @@ + + +# Card + + + +```tsx +import {Card} from '@gravity-ui/uikit'; +``` + +## Описание + +Компонент `Card` — это React-контейнер в виде карточки с возможностью настройки стилей и функций, применяемый для эстетичного и упорядоченного представления различного контента. + +## Внешний вид + +`Card` можно визуализировать с использованием различных комбинаций стилей: + +- `theme` — `normal`, `info`, `success`, `warning`, `danger` или `utility`; +- `type` — `selection`, `action` или `container`; +- `view` — `outlined` или `clear`, или `outlined`, `filled` или `raised` (в зависимости от параметра `type`). + +## `Theme` (тема) + +Параметр `theme` используется для указания стиля темы карточки. Он определяет цветовую схему карточки и ее внешний вид. + +Выбирая разные значения темы, вы можете кастомизировать внешний вид `Card` так, чтобы он соответствовал вашим целям и необходимому стилю. + +- `normal` — обычная/стандартная тема карточки; +- `info` — тема для отображения нейтральной информации; +- `success` — тема для отображения положительного или подтверждающего контента; +- `warning` — тема для отображения предупреждений; +- `danger` — тема для отображения контента, связанного с критическими проблемами или ошибками; +- `utility` — тема для отображения полезных советов. + + + +## `Type` (тип) + +Этот параметр используется для определения типа `Card`. Он позволяет настраивать внешний вид и поведение карточки. + +- `container` — карточка, которая выступает в роли контейнера для других элементов. Она обеспечивает структурированную компоновку контента. +- `action` — карточка с интерактивным элементом — например, кнопкой, которая активирует определенное действие при нажатии. +- `selection` — карточка, которую можно выбрать или нажать, чтобы выполнить определенное действие. + + + +## `View` (вид) + +Данный параметр используется для указания вида или стиля компоновки `Card`. Он позволяет настраивать внешний вид и расположение содержимого карточки: + +- `clear` — без стиля; +- `outlined` — добавляет контурную обводку для выделения содержимого карточки; +- `filled` — добавляет заливку содержимого карточки; +- `raised` — добавляет тень для создания эффекта приподнятого контейнера. + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :------------------------------------------------------------------------------------------- | :---------: | :-------------------: | +| children | Содержимое карточки. | `ReactNode` | | +| type | Тип `Card`. Определяет, какие свойства доступны. | `string` | `"container"` | +| view | Доступно только для типов `"container"` и `"selection"`. | `string` | `"outlined"` | +| theme | Базовый цвет карточки. Свойство доступно только для типа `"container"`. | `string` | `"normal"` | +| size | Размер `Card`. Определяет, какие свойства доступны. | `string` | `"m"` | +| className | CSS-класс. | `string` | | +| onClick | Обработчик клика по карточке. Свойство доступно только для типов `"selection"` и `"action"`. | `Function` | | +| selected | Выбранная карточка. Свойство доступно только для типа `"selection"`. | `Boolean` | | +| disabled | Неактивная карточка. Свойство доступно только для типов `"selection"` и `"action"`. | `Boolean` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | + +## API CSS + +| Имя | Описание | +| :-------------------------- | :----------------------- | +| `--g-card-background-color` | Цвет фона. | +| `--g-card-border-width` | Ширина границы. | +| `--g-card-border-color` | Цвет границы. | +| `--g-card-border-radius` | Радиус скругления углов. | +| `--g-card-box-shadow` | Тень. | diff --git a/src/components/Card/README.md b/src/components/Card/README.md index 8d173c8f4c..f2d951f551 100644 --- a/src/components/Card/README.md +++ b/src/components/Card/README.md @@ -10,28 +10,28 @@ import {Card} from '@gravity-ui/uikit'; ## Description -The `Card` UI component is a reusable React component that represents a card-like container with customizable styles and functionality. It is used to display information or content in a visually appealing and organized manner. +`Card` is a reusable React component that basically is a card-like container with customizable styles and features. It is used to display information or content in a visually appealing and well-organized manner. ## Appearance -`Card` can be displayed with multiple styled combination +`Card` can be displayed with multiple style combinations: -- theme (`normal`, `info`, `success`, `warning`, `danger`, `utility`) -- type (`selection`, `action`, `container`) -- view (`outlined`, `clear`) or (`outlined`, `filled`, `raised`) depends on `type` parameter +- `theme`: `normal`, `info`, `success`, `warning`, `danger`, or `utility`. +- `type`: `selection`, `action`, or `container`. +- `view`:`outlined` or `clear`, or `outlined`, `filled`, or `raised` (depending on the `type` parameter). ## Theme -This parameter is used to specify the theme style of the card. It determines the color scheme and visual appearance of the card. +This parameter is used to specify the card's theme style. It determines the card's color scheme and appearance. -By specifying different theme values, you can customize the visual appearance of the `Card` component to match the desired style and purpose. +By specifying different theme values, you can customize the `Card` visual appearance to match your purpose and the style you need. -- `normal`: represents the normal/default theme of the card. -- `info`: represents the theme for displaying informational content. -- `success`: represents the theme for displaying positive/affirmative content. -- `warning`: represents the theme for displaying warning or cautionary content. -- `danger`: represents the theme for displaying content related to danger or critical situations. -- `utility`: represents the theme for displaying utility content. +- `normal`: Normal/default theme of the card. +- `info`: Theme for displaying neutral information. +- `success`: Theme for displaying positive or affirmative content. +- `warning`: Theme for displaying warnings. +- `danger`: Theme for displaying the content related to critical issues or errors. +- `utility`: Theme for displaying useful tips. This parameter is used to define the type of the `Card` component. It allows you to customize the appearance and behavior of the card. -- `container`: represents a card that acts as a container for other elements. It provides a structured layout for content. -- `action`: represents a card with an interactive element, such as a button, that triggers an action when clicked. -- `selection`: represents a card that can be selected or clicked to perform a specific action. +- `container`: Card that acts as a container for other elements. It provides a structured layout for content. +- `action`: Card with an interactive element, such as a button, that triggers an action when clicked. +- `selection`: Card that can be selected or clicked to perform a specific action. ## View -This parameter is used to specify the view or layout style of the `Card`. It allows you to customize the appearance and arrangement of the card content. +This parameter is used to specify the `Card` view or layout style. It allows you to customize the appearance and arrangement of the card content. -- `clear`: no style will be applied. -- `outlined`: applies thin border to highlight card content. -- `filled`: fills in the card content. -- `raised`: applies a shadow to slightly lift the container. +- `clear`: No style. +- `outlined`: Applies a thin border to highlight the card content. +- `filled`: Fills in the card content. +- `raised`: Applies a shadow to slightly lift the container. ## Properties -| Name | Description | Type | Default | -| :-------- | :------------------------------------------------------------------ | :---------: | :-----------: | -| children | Card's content | `ReactNode` | | -| type | The Card type affects which properties are available | `string` | `"container"` | -| view | Available for `type`: `"container"` and `"selection"` | `string` | `"outlined"` | -| theme | Card's base color. Available for `type`: `"container"` | `string` | `"normal"` | -| size | The Card size affects which properties are available | `string` | `"m"` | -| className | CSS class | `string` | | -| onClick | Card click handler. Available for `type`: `"selection"`, `"action"` | `Function` | | -| selected | Selected card. Available for type: `"selection"` | `Boolean` | | -| disabled | Disabled card. Available for type: `"selection"`, `"action"` | `Boolean` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :-------- | :------------------------------------------------------------------------------------------------ | :---------: | :-----------: | +| children | Card content | `ReactNode` | | +| type | The `Card` type determines which properties are available. | `string` | `"container"` | +| view | This property is only available for the `"container"` and `"selection"` `type`s. | `string` | `"outlined"` | +| theme | Card's base color. This property is only available for the `"container"` `type`. | `string` | `"normal"` | +| size | The `Card` size determines which properties are available. | `string` | `"m"` | +| className | CSS class | `string` | | +| onClick | Card click handler. This property is only available for the `"selection"` and `"action"` `type`s. | `Function` | | +| selected | Selected card. This property is only available for the `"selection"` `type`. | `Boolean` | | +| disabled | Disabled card. This property is only available for the `"selection"` and `"action"` `type`s. | `Boolean` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | ## CSS API diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-chromium-linux.png index 7507788459..68ca80aeab 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-webkit-linux.png deleted file mode 100644 index f7d837b806..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-chromium-linux.png index 886f8c5f75..ff937d2c8a 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-webkit-linux.png deleted file mode 100644 index 6b8d7155e9..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-ActionType-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-chromium-linux.png index 2d40715123..3557e7ab9d 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-webkit-linux.png deleted file mode 100644 index 5c70b65912..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-chromium-linux.png index 331a0dc4b7..ff2652b415 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-webkit-linux.png deleted file mode 100644 index 865f36521a..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Custom-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-chromium-linux.png index 43f2a55171..36bc454a57 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-webkit-linux.png deleted file mode 100644 index 647fc5c936..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-chromium-linux.png index 35d61ec4ab..079231d483 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-webkit-linux.png deleted file mode 100644 index 43aad18596..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Default-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-chromium-linux.png index e046a9d371..a46fbf064d 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-webkit-linux.png deleted file mode 100644 index 0540b20c95..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-chromium-linux.png index 9748d10588..f8918af60b 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-webkit-linux.png deleted file mode 100644 index 4fbf7d63ac..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-SelectionType-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-chromium-linux.png index a2cf9033ce..6a56e743f0 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-webkit-linux.png deleted file mode 100644 index a3f46a04bd..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-chromium-linux.png index fd6085847b..72ba3d5da5 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-webkit-linux.png deleted file mode 100644 index 0a5e4b2fe4..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Size-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-chromium-linux.png index e39efa3ebc..b9eab1b6e8 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-webkit-linux.png deleted file mode 100644 index 238c2bc1d4..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-chromium-linux.png index 916b3665c0..9c3d8b848c 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-webkit-linux.png deleted file mode 100644 index a15b349cc5..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-Theme-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-chromium-linux.png index 9c4291725a..69747e992a 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-webkit-linux.png deleted file mode 100644 index f8a05b3f9f..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-chromium-linux.png index 48c1326e11..b117fff40b 100644 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-chromium-linux.png and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-webkit-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-webkit-linux.png deleted file mode 100644 index 29f624ebfe..0000000000 Binary files a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-render-story-View-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-action-type-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-action-type-light-chromium-linux.png new file mode 100644 index 0000000000..064d1906b8 Binary files /dev/null and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-action-type-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-container-type-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-container-type-light-chromium-linux.png new file mode 100644 index 0000000000..6dd126e302 Binary files /dev/null and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-container-type-light-chromium-linux.png differ diff --git a/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-selection-type-light-chromium-linux.png b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-selection-type-light-chromium-linux.png new file mode 100644 index 0000000000..94af1296de Binary files /dev/null and b/src/components/Card/__snapshots__/Card.visual.test.tsx-snapshots/Card-smoke-smoke-selection-type-light-chromium-linux.png differ diff --git a/src/components/Card/__stories__/Card.stories.tsx b/src/components/Card/__stories__/Card.stories.tsx index 2772c53a4a..8340dae624 100644 --- a/src/components/Card/__stories__/Card.stories.tsx +++ b/src/components/Card/__stories__/Card.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {action} from '@storybook/addon-actions'; import type {Meta, StoryObj} from '@storybook/react'; diff --git a/src/components/Card/__tests__/Card.test.tsx b/src/components/Card/__tests__/Card.test.tsx index ba011e60db..f91b5a51cc 100644 --- a/src/components/Card/__tests__/Card.test.tsx +++ b/src/components/Card/__tests__/Card.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Card/__tests__/Card.visual.test.tsx b/src/components/Card/__tests__/Card.visual.test.tsx index c264a8bcfa..2f2ae7abcb 100644 --- a/src/components/Card/__tests__/Card.visual.test.tsx +++ b/src/components/Card/__tests__/Card.visual.test.tsx @@ -1,10 +1,20 @@ -import React from 'react'; - -import {test} from '~playwright/core'; - +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {CardProps} from '../Card'; +import {Card} from '../Card'; + +import { + containerViewCases, + disabledCases, + selectedCases, + selectionViewCases, + sizeCases, + themeCases, +} from './cases'; import {CardStories} from './helpersPlaywright'; -test.describe('Card', () => { +test.describe('Card', {tag: '@Card'}, () => { test('render story: ', async ({mount, expectScreenshot}) => { await mount(); @@ -46,4 +56,130 @@ test.describe('Card', () => { await expectScreenshot(); }); + + smokeTest('smoke, selection type', async ({mount, expectScreenshot}) => { + const defaultProps: CardProps = { + children: null, + }; + + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + type: 'selection', + } as const, + { + size: sizeCases, + disabled: disabledCases, + selected: selectedCases, + view: selectionViewCases, + }, + { + scenarioName: 'selection', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+ Some text +
+
+
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('smoke, action type', async ({mount, expectScreenshot}) => { + const defaultProps: CardProps = { + children: null, + }; + + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + type: 'action', + onClick: () => {}, + } as const, + { + size: sizeCases, + disabled: disabledCases, + }, + { + scenarioName: 'selection', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+ Some text +
+
+
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('smoke, container type', async ({mount, expectScreenshot}) => { + const defaultProps: CardProps = { + children: null, + }; + + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + type: 'container', + } as const, + { + size: sizeCases, + disabled: disabledCases, + view: containerViewCases, + theme: themeCases, + }, + { + scenarioName: 'selection', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
Some text
+
+
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); }); diff --git a/src/components/Card/__tests__/cases.tsx b/src/components/Card/__tests__/cases.tsx new file mode 100644 index 0000000000..f0a98fdbf5 --- /dev/null +++ b/src/components/Card/__tests__/cases.tsx @@ -0,0 +1,20 @@ +import type {Cases, CasesWithName} from '../../../stories/tests-factory/models'; +import type {CardProps} from '../Card'; + +export const sizeCases: Array = ['m', 'l']; + +export const disabledCases: CasesWithName = [['disabled', true]]; + +export const selectedCases: CasesWithName = [['selected', true]]; + +export const selectionViewCases: Cases = ['outlined', 'clear']; +export const containerViewCases: Cases = ['outlined', 'filled', 'raised']; + +export const themeCases: Cases = [ + 'normal', + 'info', + 'success', + 'warning', + 'danger', + 'utility', +]; diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 958962fd0c..21c0b4678f 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useCheckbox} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; diff --git a/src/components/Checkbox/CheckboxDashIcon.tsx b/src/components/Checkbox/CheckboxDashIcon.tsx index c35e79e098..44a6cd098b 100644 --- a/src/components/Checkbox/CheckboxDashIcon.tsx +++ b/src/components/Checkbox/CheckboxDashIcon.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; export function CheckboxDashIcon(props: React.SVGProps) { return ( diff --git a/src/components/Checkbox/CheckboxTickIcon.tsx b/src/components/Checkbox/CheckboxTickIcon.tsx index 9e5d94192a..1760d55802 100644 --- a/src/components/Checkbox/CheckboxTickIcon.tsx +++ b/src/components/Checkbox/CheckboxTickIcon.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; export function CheckboxTickIcon(props: React.SVGProps) { return ( diff --git a/src/components/Checkbox/README-ru.md b/src/components/Checkbox/README-ru.md new file mode 100644 index 0000000000..246bef13b5 --- /dev/null +++ b/src/components/Checkbox/README-ru.md @@ -0,0 +1,157 @@ + + +# Checkbox + + + +```tsx +import {Checkbox} from '@gravity-ui/uikit'; +``` + +Компонент `Checkbox` позволяет пользователю выбрать или отменить выбор определенного значения. + +## Состояния + +`Checkbox` может иметь разные состояния: + +- `Checked` — чекбокс отмечен галочкой. +- `Disabled` — чекбокс недоступен. +- `Indeterminate` — чекбокс находится в промежуточном состоянии между отмеченным и неотмеченным. + + + + + +```tsx +Unchecked +Checked +Disabled +Indeterminate +``` + + + +## Размер + +Размер `Checkbox` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx +M Size +L Size +``` + + + +## Лейбл + +Лейбл для `Checkbox` можно задать через свойство `content` или передать его как дочерний элемент. + + + + + +```tsx +
+ +
+ + Content as children + +
+
+``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-------------------: | +| children | Содержимое чекбокса (как правило, лейбл). | `ReactNode` | | +| content | Содержимое чекбокса (альтернатива `children`). | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у чекбокса. | `boolean` | `false` | +| checked | Включает или отключает состояние `checked` у чекбокса. | `boolean` | `false` | +| defaultChecked | Задает начальное состояние `checked` при монтировании компонента. | `boolean` | `false` | +| onUpdate | Срабатывает при изменении состояния чекбокса пользователем и передает значение `checked` как аргумент обратного вызова. | `(checked: boolean) => void` | | +| onChange | Срабатывает при изменении состояния чекбокса пользователем и передает событие изменения как аргумент обратного вызова. | `Function` | | +| onFocus | Обработчик события, вызываемый, когда элемент ввода чекбокса получает фокус. | `Function` | | +| onBlur | Обработчик события, вызываемый, когда элемент ввода чекбокса теряет фокус. | `Function` | | +| size | Определяет размер чекбокса. | `m` `l` | `m` | +| id | HTML-атрибут `id`. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | HTML-атрибут `class`. | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| name | HTML-атрибут `name` для элемента ввода. | `string` | | +| value | HTML-атрибут `value` для элемента ввода. | `string` | | +| indeterminate | Включает или отключает состояние `indeterminate` у чекбокса. | `boolean` | `false` | +| controlProps | Дополнительные свойства базового элемента ввода. | `React.InputHTMLAttributes` | | +| controlRef | Ссылка на базовый элемент ввода. | `React.Ref` | | diff --git a/src/components/Checkbox/README.md b/src/components/Checkbox/README.md index 98e12dac19..f5c7088681 100644 --- a/src/components/Checkbox/README.md +++ b/src/components/Checkbox/README.md @@ -12,11 +12,11 @@ The `Checkbox` component allows the user to select or deselect a specific value. ## States -The Checkbox can be in different states. +A `Checkbox` can have different states: -- Checked - when the checkbox is selected. -- Disabled - when the checkbox is unavailable for interaction. -- Indeterminate - when the checkbox is in an intermediate state between being selected and unselected. +- Checked: The checkbox is ticked. +- Disabled: The checkbox is unavailable. +- Indeterminate: The checkbox is in an intermediate state between being ticked and unticked. ## Size -To control the size of the `Checkbox`, use the `size` property. The default size is `m`. +Use the `size` property to manage the `Checkbox` size. The default size is `m`. ## Label -You can set a label for a `Checkbox` component using the `content` property or pass it as children. +You can assign a label to a `Checkbox` using the `content` property or provide it as a child property. ## Properties -| Name | Description | Type | Default | -| :------------- | :--------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----: | -| children | The content of the checkbox (usually a label). | `ReactNode` | | -| content | The content of the checkbox (alternative to children). | `ReactNode` | | -| disabled | Toggles the `disabled` state of the checkbox. | `boolean` | `false` | -| checked | Toggles the checked state of the checkbox. | `boolean` | `false` | -| defaultChecked | Sets the initial checked state when the component is mounted. | `boolean` | `false` | -| onUpdate | Fires when the user changes the checkbox state. Provides the checked value as a callback's argument. | `(checked: boolean) => void` | | -| onChange | Fires when the user changes the checkbox state. Provides the change event as a callback's argument. | `Function` | | -| onFocus | Event handler for when the checkbox input element receives focus. | `Function` | | -| onBlur | Event handler for when the checkbox input element loses focus. | `Function` | | -| size | Sets the size of the checkbox. | `m` `l` | `m` | -| id | HTML `id` attribute | `string` | | -| qa | HTML `data-qa` attribute, used in tests. | `string` | | -| style | HTML `style` attribute | `React.CSSProperties` | | -| className | HTML `class` attribute | `string` | | -| title | HTML `title` attribute | `string` | | -| name | HTML `name` attribute for the input element. | `string` | | -| value | HTML `value` attribute for the input element. | `string` | | -| indeterminate | Toggles the indeterminate state of the checkbox. | `boolean` | `false` | -| controlProps | Additional props for the underlying input element. | `React.InputHTMLAttributes` | | -| controlRef | Ref to the underlying input element. | `React.Ref` | | +| Name | Description | Type | Default | +| :------------- | :---------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----: | +| children | Checkbox content (usually, a label). | `ReactNode` | | +| content | Checkbox content (alternative to children). | `ReactNode` | | +| disabled | Toggles the `disabled` state of the checkbox. | `boolean` | `false` | +| checked | Toggles the `checked` state of the checkbox. | `boolean` | `false` | +| defaultChecked | Sets the initial checked state when the component is mounted. | `boolean` | `false` | +| onUpdate | Fires when the user changes the checkbox state and provides the checked value as a callback argument. | `(checked: boolean) => void` | | +| onChange | Fires when the user changes the checkbox state and provides the change event as a callback argument. | `Function` | | +| onFocus | Event handler to use when the checkbox input element receives focus. | `Function` | | +| onBlur | Event handler to use when the checkbox input element loses focus. | `Function` | | +| size | Determines the checkbox size. | `m` `l` | `m` | +| id | `id` HTML attribute | `string` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| className | `class` HTML attribute | `string` | | +| title | `title` HTML attribute | `string` | | +| name | `name` HTML attribute for the input element. | `string` | | +| value | `value` HTML attribute for the input element. | `string` | | +| indeterminate | Toggles the `indeterminate` state of the checkbox. | `boolean` | `false` | +| controlProps | Additional propeties for the underlying input element. | `React.InputHTMLAttributes` | | +| controlRef | Ref to the underlying input element. | `React.Ref` | | diff --git a/src/components/Checkbox/__stories__/Checkbox.stories.tsx b/src/components/Checkbox/__stories__/Checkbox.stories.tsx index 0841787f33..ecf871e33f 100644 --- a/src/components/Checkbox/__stories__/Checkbox.stories.tsx +++ b/src/components/Checkbox/__stories__/Checkbox.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; import {Showcase} from '../../../demo/Showcase'; diff --git a/src/components/Checkbox/__stories__/CheckboxShowcase.tsx b/src/components/Checkbox/__stories__/CheckboxShowcase.tsx index 0da525b35c..3d8c49e084 100644 --- a/src/components/Checkbox/__stories__/CheckboxShowcase.tsx +++ b/src/components/Checkbox/__stories__/CheckboxShowcase.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Showcase} from '../../../demo/Showcase'; import {ShowcaseItem} from '../../../demo/ShowcaseItem'; import {Checkbox} from '../Checkbox'; diff --git a/src/components/Checkbox/__tests__/Checkbox.test.tsx b/src/components/Checkbox/__tests__/Checkbox.test.tsx index 663f538230..dcf57edfdc 100644 --- a/src/components/Checkbox/__tests__/Checkbox.test.tsx +++ b/src/components/Checkbox/__tests__/Checkbox.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Checkbox/__tests__/Checkbox.visual.test.tsx b/src/components/Checkbox/__tests__/Checkbox.visual.test.tsx index 5dfbc86712..a30f9ba1e3 100644 --- a/src/components/Checkbox/__tests__/Checkbox.visual.test.tsx +++ b/src/components/Checkbox/__tests__/Checkbox.visual.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {smokeTest, test} from '~playwright/core'; import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; diff --git a/src/components/ClipboardButton/ClipboardButton.tsx b/src/components/ClipboardButton/ClipboardButton.tsx index c30d42065e..56a1e34643 100644 --- a/src/components/ClipboardButton/ClipboardButton.tsx +++ b/src/components/ClipboardButton/ClipboardButton.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {ActionTooltip} from '../ActionTooltip'; import {Button} from '../Button'; diff --git a/src/components/ClipboardButton/README-ru.md b/src/components/ClipboardButton/README-ru.md new file mode 100644 index 0000000000..82834edc21 --- /dev/null +++ b/src/components/ClipboardButton/README-ru.md @@ -0,0 +1,51 @@ + + +# ClipboardButton + + + +```tsx +import {ClipboardButton} from '@gravity-ui/uikit'; +``` + +`ClipboardButton` — компонент, объединяющий [`CopyToClipboard`](../CopyToClipboard/README.md) и [`ClipboardIcon`](../ClipboardIcon/README.md). [`CopyToClipboard`](../CopyToClipboard/README.md) отправляет текст в буфер обмена и использует [`ClipboardIcon`](../ClipboardIcon/README.md) для отображения анимации во время копирования. + + + + + +```tsx + +``` + + + +## Свойства + +`ClipboardButton` наследует свойства от `Button`, за исключением `href`, `component`, `target`, `rel`, `loading` и `children`. + +| Имя | Описание | Тип | Значение по умолчанию | +| :----------------- | :-------------------------------------------------------------------------- | :-----------------------------------------------: | :-------------------: | +| hasTooltip | Включает или отключает отображение тултипа. | `boolean` | `true` | +| onCopy | Обратный вызов после копирования:`(text: string, result: boolean) => void`. | `Function` | | +| options | Параметры копирования в буфер обмена. | [CopyToClipboardOptions](#copytoclipboardoptions) | | +| text | Копируемый текст. | `string` | | +| timeout | Время до возврата состояния в норму после клика по кнопке. | `number` | `1000` | +| tooltipInitialText | Текст, отображаемый перед копированием. | `string` | `"Copy"` | +| tooltipSuccessText | Текст, отображаемый после копирования. | `string` | `"Copied!"` | + +### CopyToClipboardOptions + +| Имя | Описание | Тип | Значение по умолчанию | +| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: | :-------------------: | +| debug | Включает вывод в консоль. | `boolean` | `false` | +| message | Сообщение-подсказка. | `string` | | +| format | Устанавливает MIME-тип для копирования. Используйте `text/html` для копирования в формате HTML и `text/plain`, если при вставке в текстовый редактор не нужно наследовать стили. | `string` | `"text/html"` | diff --git a/src/components/ClipboardButton/README.md b/src/components/ClipboardButton/README.md index a46c432de4..1c24b45484 100644 --- a/src/components/ClipboardButton/README.md +++ b/src/components/ClipboardButton/README.md @@ -8,7 +8,7 @@ import {ClipboardButton} from '@gravity-ui/uikit'; ``` -A component that puts all together: [`CopyToClipboard`](../CopyToClipboard/README.md) and [`ClipboardIcon`](../ClipboardIcon/README.md). [`CopyToClipboard`](../CopyToClipboard/README.md) component takes given text into the clipboard and as a wrapper uses [`ClipboardIcon`](../ClipboardIcon/README.md) as a content for itself to display animation when copy-paste happening. +This component puts [`CopyToClipboard`](../CopyToClipboard/README.md) and [`ClipboardIcon`](../ClipboardIcon/README.md) together. [`CopyToClipboard`](../CopyToClipboard/README.md) sends a text to the clipboard and, as a wrapper, uses [`ClipboardIcon`](../ClipboardIcon/README.md) as content for itself to display animation when a copy-paste event happens. ## Properties -`ClipboardButton` properties are inherited from `Button` [properties](../Button/README.md#properties) except `href`, `component`, `target`, `rel`, `loading`, `children`. +The `ClipboardButton` properties are inherited from the `Button` [properties](../Button/README.md#properties), except for `href`, `component`, `target`, `rel`, `loading`, and `children`. -| Name | Description | Type | Default | -| :----------------- | :----------------------------------------------------------------------- | :-----------------------------------------------: | :---------: | -| hasTooltip | Disable tooltip. Tooltip won't be shown | `boolean` | `true` | -| onCopy | Callback after copy `(text: string, result: boolean) => void` | `Function` | | -| options | Copy to clipboard options | [CopyToClipboardOptions](#copytoclipboardoptions) | | -| text | Text to copy | `string` | | -| timeout | Time before state bounces back to its normal after the button is clicked | `number` | `1000` | -| tooltipInitialText | Text shown before copy | `string` | `"Copy"` | -| tooltipSuccessText | Text shown after copy | `string` | `"Copied!"` | +| Name | Description | Type | Default | +| :----------------- | :------------------------------------------------------------------------ | :-----------------------------------------------: | :---------: | +| hasTooltip | Toggles displaying the tooltip | `boolean` | `true` | +| onCopy | Callback after copying `(text: string, result: boolean) => void` | `Function` | | +| options | Copy to clipboard options | [CopyToClipboardOptions](#copytoclipboardoptions) | | +| text | Text to copy | `string` | | +| timeout | Time before the state switches back to normal after the button is clicked | `number` | `1000` | +| tooltipInitialText | Text shown before copying | `string` | `"Copy"` | +| tooltipSuccessText | Text shown after copying | `string` | `"Copied!"` | ### CopyToClipboardOptions | Name | Description | Type | Default | | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------: | :-----------: | -| debug | Enable output to console | `boolean` | `false` | +| debug | Enables output to console | `boolean` | `false` | | message | Prompt message | `string` | | -| format | Set the MIME type of what you want to copy as. Use `text/html` to copy as HTML, `text/plain` to avoid inherited styles showing when pasted into rich text editor. | `string` | `"text/html"` | +| format | Set the MIME type of what you want to copy. Use `text/html` to copy as HTML and `text/plain` to avoid showing inherited styles when pasted into rich text editor. | `string` | `"text/html"` | diff --git a/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-copy-light-chromium-linux.png b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-copy-light-chromium-linux.png new file mode 100644 index 0000000000..b2e97a63c2 Binary files /dev/null and b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-copy-light-chromium-linux.png differ diff --git a/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-hover-light-chromium-linux.png b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-hover-light-chromium-linux.png new file mode 100644 index 0000000000..a44581adf3 Binary files /dev/null and b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-after-hover-light-chromium-linux.png differ diff --git a/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-light-chromium-linux.png b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..de4268c20a Binary files /dev/null and b/src/components/ClipboardButton/__snapshots__/ClipboardButton.visual.test.tsx-snapshots/ClipboardButton-smoke-light-chromium-linux.png differ diff --git a/src/components/ClipboardButton/__stories__/ClipboardButton.stories.tsx b/src/components/ClipboardButton/__stories__/ClipboardButton.stories.tsx index ad6d56996d..9d759e8f2e 100644 --- a/src/components/ClipboardButton/__stories__/ClipboardButton.stories.tsx +++ b/src/components/ClipboardButton/__stories__/ClipboardButton.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; import {Showcase} from '../../../demo/Showcase'; diff --git a/src/components/ClipboardButton/__tests__/ClipboardButton.test.tsx b/src/components/ClipboardButton/__tests__/ClipboardButton.test.tsx index 55010e481f..c3334a4a04 100644 --- a/src/components/ClipboardButton/__tests__/ClipboardButton.test.tsx +++ b/src/components/ClipboardButton/__tests__/ClipboardButton.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import userEvent from '@testing-library/user-event'; import {act, render, screen} from '../../../../test-utils/utils'; diff --git a/src/components/ClipboardButton/__tests__/ClipboardButton.visual.test.tsx b/src/components/ClipboardButton/__tests__/ClipboardButton.visual.test.tsx new file mode 100644 index 0000000000..e51d2e0133 --- /dev/null +++ b/src/components/ClipboardButton/__tests__/ClipboardButton.visual.test.tsx @@ -0,0 +1,40 @@ +import {smokeTest, test} from '~playwright/core'; + +import type {ClipboardButtonProps} from '../ClipboardButton'; +import {ClipboardButton} from '../ClipboardButton'; + +test.describe('ClipboardButton', {tag: '@ClipboardButton'}, () => { + const defaultProps: ClipboardButtonProps = { + text: 'Text', + onCopy: () => {}, + }; + + smokeTest('', async ({mount, page, expectScreenshot}) => { + const root = await mount( +
+ +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + + await root.locator("button[type='button']").hover(); + + // wait for render tooltip + await page.waitForTimeout(1000); + + await expectScreenshot({ + themes: ['light'], + nameSuffix: 'after hover', + }); + + await root.locator("button[type='button']").click(); + + await expectScreenshot({ + themes: ['light'], + nameSuffix: 'after copy', + }); + }); +}); diff --git a/src/components/ClipboardIcon/ClipboardIcon.tsx b/src/components/ClipboardIcon/ClipboardIcon.tsx index 31177ca0e9..2cb6f42712 100644 --- a/src/components/ClipboardIcon/ClipboardIcon.tsx +++ b/src/components/ClipboardIcon/ClipboardIcon.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Copy, CopyCheck, CopyXmark} from '@gravity-ui/icons'; import type {CopyToClipboardStatus} from '../CopyToClipboard/types'; diff --git a/src/components/ClipboardIcon/README-ru.md b/src/components/ClipboardIcon/README-ru.md new file mode 100644 index 0000000000..f2961eb891 --- /dev/null +++ b/src/components/ClipboardIcon/README-ru.md @@ -0,0 +1,48 @@ + + +# ClipboardIcon + + + +```tsx +import {ClipboardIcon} from '@gravity-ui/uikit'; +``` + +Этот компонент в основном используется вместе с `CopyToClipboard` в качестве обертки. + +### Статус + +Иконка будет изменяться в зависимости от значения свойства `status`. + + + + + +```tsx + + {(status) => } + +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :------------------------------------------------- | :------- | :-------------------- | +| className | HTML-атрибут `class`. | `string` | | +| size | Определяет размер иконки. | `number` | | +| status | Принимает значения `pending`, `success` и `error`. | `string` | | diff --git a/src/components/ClipboardIcon/README.md b/src/components/ClipboardIcon/README.md index 5a8f13f097..6f3a08192d 100644 --- a/src/components/ClipboardIcon/README.md +++ b/src/components/ClipboardIcon/README.md @@ -8,11 +8,11 @@ import {ClipboardIcon} from '@gravity-ui/uikit'; ``` -This component is mainly used together with `CopyToClipboard` as wrap component. +This component is mainly used along with `CopyToClipboard` as a wrap component. ### Status -Depends on `status` property icon is changed. +Depending on the `status` property, the icon will be changing accordingly: ## Properties -| Name | Description | Type | Default | -| :-------- | :---------------------------------------------- | :------- | :------ | -| className | HTML `class` attribute | `string` | | -| size | Sets icon size | `number` | | -| status | Available values: `pending`, `success`, `error` | `string` | | +| Name | Description | Type | Default | +| :-------- | :---------------------------------------------------------- | :------- | :------ | +| className | `class` HTML attribute | `string` | | +| size | Determines the icon size | `number` | | +| status | The available values are `pending`, `success`, and `error`. | `string` | | diff --git a/src/components/ClipboardIcon/__snapshots__/ClipboardIcon.visual.test.tsx-snapshots/ClipboardIcon-smoke-smoke-light-chromium-linux.png b/src/components/ClipboardIcon/__snapshots__/ClipboardIcon.visual.test.tsx-snapshots/ClipboardIcon-smoke-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..3e8ac63862 Binary files /dev/null and b/src/components/ClipboardIcon/__snapshots__/ClipboardIcon.visual.test.tsx-snapshots/ClipboardIcon-smoke-smoke-light-chromium-linux.png differ diff --git a/src/components/ClipboardIcon/__tests__/ClipboardIcon.visual.test.tsx b/src/components/ClipboardIcon/__tests__/ClipboardIcon.visual.test.tsx new file mode 100644 index 0000000000..5f3238cb95 --- /dev/null +++ b/src/components/ClipboardIcon/__tests__/ClipboardIcon.visual.test.tsx @@ -0,0 +1,37 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {ClipboardIconProps} from '../ClipboardIcon'; +import {ClipboardIcon} from '../ClipboardIcon'; + +import {sizeCases, statusCases} from './cases'; + +test.describe('ClipboardIcon', {tag: '@ClipboardIcon'}, () => { + smokeTest('smoke', async ({mount, expectScreenshot}) => { + const defaultProps: ClipboardIconProps = { + status: 'success', + }; + + const smokeScenarios = createSmokeScenarios(defaultProps, { + size: sizeCases, + status: statusCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); +}); diff --git a/src/components/ClipboardIcon/__tests__/cases.tsx b/src/components/ClipboardIcon/__tests__/cases.tsx new file mode 100644 index 0000000000..c2d2bcc359 --- /dev/null +++ b/src/components/ClipboardIcon/__tests__/cases.tsx @@ -0,0 +1,6 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {ClipboardIconProps} from '../ClipboardIcon'; + +export const sizeCases: Array = [10, 20, 30]; + +export const statusCases: Cases = ['pending', 'success', 'error']; diff --git a/src/components/ControlLabel/ControlLabel.tsx b/src/components/ControlLabel/ControlLabel.tsx index 2042aac340..3e727592e6 100644 --- a/src/components/ControlLabel/ControlLabel.tsx +++ b/src/components/ControlLabel/ControlLabel.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {block} from '../utils/cn'; diff --git a/src/components/CopyToClipboard/CopyToClipboard.tsx b/src/components/CopyToClipboard/CopyToClipboard.tsx index 6265056c99..189fa945b6 100644 --- a/src/components/CopyToClipboard/CopyToClipboard.tsx +++ b/src/components/CopyToClipboard/CopyToClipboard.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import ReactCopyToClipboard from 'react-copy-to-clipboard'; diff --git a/src/components/CopyToClipboard/README-ru.md b/src/components/CopyToClipboard/README-ru.md new file mode 100644 index 0000000000..8c90970593 --- /dev/null +++ b/src/components/CopyToClipboard/README-ru.md @@ -0,0 +1,80 @@ + + +# CopyToClipboard + + + +```tsx +import {CopyToClipboard} from '@gravity-ui/uikit'; +``` + +`CopyToClipboard` — это оберточный компонент, который копирует текст в буфер обмена и может изменять свое содержимое в зависимости от возвращенного статуса. + +### Children (функция рендеринга) + +Данная функция рендеринга передается в виде свойства `children`. Она может изменять свое содержимое в зависимости от статуса, возвращаемого в качестве первого аргумента. +Доступны три статуса — `pending`, `success` и `error`: + +`pending` — исходный статус, возвращаемый функцией рендеринга в нейтральном состоянии; + +`success` — статус результата, возвращаемый функцией рендеринга в случае успешного выполнения; + +`error` — статус результата, возвращаемый функцией рендеринга в случае ошибки. + +Параметр `timeout` устанавливает время в миллисекундах для возврата к исходному статусу (`pending`) после получения любого из статусов результата (`success` или `error`). + + + + + +```tsx +const buttonText = { + pending: 'Click Me', + success: 'Copied!', + error: "Couldn't copy...", +}; + + + {(status) => } +; +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------- | :-------------------------------------------------------------------------- | :--------: | :-------------------: | +| children | Функция рендеринга `(status: CopyToClipboardStatus) => React.ReactElement`. | `Function` | | +| onCopy | Обработчик события `copy`. | `Function` | | +| text | Копируемый текст. | `string` | | +| timeout | Время в миллисекундах для возврата к исходному статусу. | `number` | | diff --git a/src/components/CopyToClipboard/README.md b/src/components/CopyToClipboard/README.md index 8141e47215..3646f7a01c 100644 --- a/src/components/CopyToClipboard/README.md +++ b/src/components/CopyToClipboard/README.md @@ -8,20 +8,20 @@ import {CopyToClipboard} from '@gravity-ui/uikit'; ``` -CopyToClipboard is a wrapper component that copies given text to clipboard and can update its content depends on returned status. +CopyToClipboard is a wrapper component that copies text to clipboard and can update its content depending on the returned status. ### Children (render function) -The render function passed as children props and can update its content depends on status that is returned as first argument in render function. -There are 3 available statuses: pending, success, error +This is a render function provided as children properties. It can update its content depending on the status that is returned as the first argument in the render function. +There are three available statuses: pending, success, and error. -`pending` - the initial status returned in render function in neutral case +`pending`: Initial status returned in the render function in the neutral case. -`success` - the result status returned in render function in success case +`success`: Result status returned in the render function in case of success. -`error` - the result status returned in render function in error case +`error`: Result status returned in the render function in case of error. -Option `timeout` set the time in ms to restore initial (`pending`) status after one of the result statuses (`success` or `error`). +The `timeout` option sets the time in ms to restore the initial (`pending`) status after one of the result statuses (`success` or `error`). + +# Divider + + + +```tsx +import {Divider} from '@gravity-ui/uikit'; +``` + +Компонент `Divider` (разделитель) представляет собой тонкую линию для разграничения и группировки элементов с целью усиления их визуальной иерархии. + +```tsx + +``` + +### Ориентация + +Ориентацию `Divider` можно задать с помощью свойства `direction`. Ориентация по умолчанию — `horizontal`. + +#### Горизонтальная ориентация + + + + + +```tsx +import {Container, Text, Divider} from '@gravity-ui/uikit'; + + + + Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam, officiis! Fugit minus ea, + perferendis eum consectetur quae vitae. Aliquid, quam reprehenderit? Maiores sed pariatur + aliquid commodi atque sunt officiis natus? + + + + Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aperiam, officiis! Fugit minus ea, + perferendis eum consectetur quae vitae. Aliquid, quam reprehenderit? Maiores sed pariatur + aliquid commodi atque sunt officiis natus? + +; +``` + + + +#### Вертикальная ориентация + + + + + +```tsx +import {Flex, Label, Divider} from '@gravity-ui/uikit'; + + + + + + + + + + + +; +``` + + + +### Пользовательский контент + + + + + +```tsx +import {Divider, Flex, Container, Icon} from '@gravity-ui/uikit'; +import {CheckIcon} from '@gravity-ui/icons'; + + + + Custom content + + + + +; +``` + + + +### Выравнивание + + + + + +```tsx +import {Divider, Flex, Container} from '@gravity-ui/uikit'; + + + + Start content + Center content + End content + +; +``` + + + +### Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :---------- | :----------------------------------------------------- | :---------------------- | :-------------------- | +| className | HTML-атрибут `class`. | `string` | - | +| orientation | Определяет направление разделителя. | `horizontal - vertical` | `horizontal` | +| children | Пользовательский контент внутри разделителя. | `React.ReactNode` | | +| align | Расположение пользовательского контента. | `start - center - end` | `start` | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | + +### API CSS + +| Имя | Описание | +| :------------------ | :---------------- | +| `--g-divider-color` | Цвет разделителя. | diff --git a/src/components/Divider/README.md b/src/components/Divider/README.md index dbb176dbd7..c0e763b55f 100644 --- a/src/components/Divider/README.md +++ b/src/components/Divider/README.md @@ -16,7 +16,7 @@ The `Divider` component is used as a thin line for delimiting and grouping eleme ### Orientation -To control the orientation of the `Divider`, use the `orientation` property. The default orientation is `horizontal`. +Use the `direction` property to manage the `Divider` orientation. The default orientation is `horizontal`. #### Horizontal @@ -222,14 +222,14 @@ import {Divider, Flex, Container} from '@gravity-ui/uikit'; ### Properties -| Name | Description | Type | Default | -| :---------- | :-------------------------------------- | :---------------------- | :----------- | -| className | HTML `class` attribute | `string` | - | -| orientation | Sets the direction of divider | `horizontal - vertical` | `horizontal` | -| children | Custom content inside divider | `React.ReactNode` | | -| align | Custom content position | `start - center - end` | `start` | -| style | HTML `style` attribute | `React.CSSProperties` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :---------- | :----------------------------------------- | :---------------------- | :----------- | +| className | `class` HTML attribute | `string` | - | +| orientation | Determines the divider direction | `horizontal - vertical` | `horizontal` | +| children | Custom content inside the divider | `React.ReactNode` | | +| align | Custom content position | `start - center - end` | `start` | +| style | `style` HTML attribute | `React.CSSProperties` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | ### CSS API diff --git a/src/components/Divider/__snapshots__/Divider.visual.test.tsx-snapshots/Divider-smoke-smoke-light-chromium-linux.png b/src/components/Divider/__snapshots__/Divider.visual.test.tsx-snapshots/Divider-smoke-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..a25f526a08 Binary files /dev/null and b/src/components/Divider/__snapshots__/Divider.visual.test.tsx-snapshots/Divider-smoke-smoke-light-chromium-linux.png differ diff --git a/src/components/Divider/__stories__/Divider.stories.tsx b/src/components/Divider/__stories__/Divider.stories.tsx index 2c4f63acac..11decd0166 100644 --- a/src/components/Divider/__stories__/Divider.stories.tsx +++ b/src/components/Divider/__stories__/Divider.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Check as CheckIcon} from '@gravity-ui/icons'; import type {Meta, StoryObj} from '@storybook/react'; diff --git a/src/components/Divider/__tests__/Divider.test.tsx b/src/components/Divider/__tests__/Divider.test.tsx index c690792829..294a1632b0 100644 --- a/src/components/Divider/__tests__/Divider.test.tsx +++ b/src/components/Divider/__tests__/Divider.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {render, screen} from '../../../../test-utils/utils'; import {block} from '../../utils/cn'; import {Divider} from '../Divider'; diff --git a/src/components/Divider/__tests__/Divider.visual.test.tsx b/src/components/Divider/__tests__/Divider.visual.test.tsx new file mode 100644 index 0000000000..275b494bc8 --- /dev/null +++ b/src/components/Divider/__tests__/Divider.visual.test.tsx @@ -0,0 +1,34 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {DividerProps} from '../Divider'; + +import {orientationCases} from './cases'; +import {ListWithDivider} from './helpers'; + +test.describe('Divider', {tag: '@Divider'}, () => { + smokeTest('smoke', async ({mount, expectScreenshot}) => { + const defaultProps: DividerProps = {}; + + const smokeScenarios = createSmokeScenarios(defaultProps, { + orientation: orientationCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); +}); diff --git a/src/components/Divider/__tests__/cases.tsx b/src/components/Divider/__tests__/cases.tsx new file mode 100644 index 0000000000..20fade9df4 --- /dev/null +++ b/src/components/Divider/__tests__/cases.tsx @@ -0,0 +1,4 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {DividerProps} from '../Divider'; + +export const orientationCases: Cases = ['vertical', 'horizontal']; diff --git a/src/components/Divider/__tests__/helpers.tsx b/src/components/Divider/__tests__/helpers.tsx new file mode 100644 index 0000000000..29ecbf2c23 --- /dev/null +++ b/src/components/Divider/__tests__/helpers.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; + +import {Card} from '../../Card'; +import {ListItem} from '../../List'; +import {Flex} from '../../layout'; +import type {DividerProps} from '../Divider'; +import {Divider} from '../Divider'; + +const listItems = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']; + +export const ListWithDivider = (props: DividerProps) => { + return ( + + + {listItems.map((value, index) => ( + + {}} + /> + + {index < listItems.length - 1 && } + + ))} + + + ); +}; diff --git a/src/components/DropdownMenu/DropdownMenu.test.tsx b/src/components/DropdownMenu/DropdownMenu.test.tsx index 100165d568..d14cbc65e2 100644 --- a/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import userEvent from '@testing-library/user-event'; import {render, screen} from '../../../test-utils/utils'; diff --git a/src/components/DropdownMenu/DropdownMenu.tsx b/src/components/DropdownMenu/DropdownMenu.tsx index d8268ca2eb..282d4b028b 100644 --- a/src/components/DropdownMenu/DropdownMenu.tsx +++ b/src/components/DropdownMenu/DropdownMenu.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Ellipsis} from '@gravity-ui/icons'; diff --git a/src/components/DropdownMenu/DropdownMenuContext.ts b/src/components/DropdownMenu/DropdownMenuContext.ts index c21360b278..f3c59aa553 100644 --- a/src/components/DropdownMenu/DropdownMenuContext.ts +++ b/src/components/DropdownMenu/DropdownMenuContext.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; interface Context { toggle(open?: boolean): void; diff --git a/src/components/DropdownMenu/DropdownMenuItem.tsx b/src/components/DropdownMenu/DropdownMenuItem.tsx index a202c91288..3ed9deab4f 100644 --- a/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {ChevronLeft, ChevronRight} from '@gravity-ui/icons'; diff --git a/src/components/DropdownMenu/DropdownMenuNavigationContext.tsx b/src/components/DropdownMenu/DropdownMenuNavigationContext.tsx index 1638da5604..1cb70d441c 100644 --- a/src/components/DropdownMenu/DropdownMenuNavigationContext.tsx +++ b/src/components/DropdownMenu/DropdownMenuNavigationContext.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; export type DropdownMenuNavigationContextType = { activeMenuPath: number[]; diff --git a/src/components/DropdownMenu/DropdownMenuPopup.tsx b/src/components/DropdownMenu/DropdownMenuPopup.tsx index 6d356ce9dc..ae2cc0f799 100644 --- a/src/components/DropdownMenu/DropdownMenuPopup.tsx +++ b/src/components/DropdownMenu/DropdownMenuPopup.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useListNavigation} from '../../hooks'; import {Menu} from '../Menu'; diff --git a/src/components/DropdownMenu/README-ru.md b/src/components/DropdownMenu/README-ru.md new file mode 100644 index 0000000000..0342f9c526 --- /dev/null +++ b/src/components/DropdownMenu/README-ru.md @@ -0,0 +1,556 @@ + + +# DropdownMenu + + + +```tsx +import {DropdownMenu} from '@gravity-ui/uikit'; +``` + +Компонент `DropdownMenu` (выпадающее меню) позволяет организовывать элементы в группы, создавать подменю и настраивать переключатель. Элементы выпадающего меню настраиваются через свойство `items`. По умолчанию переключатель меню — кнопка с иконкой многоточия (**⋯**), которую можно переопределить с помощью свойства `renderSwitcher`. + + + + + +```jsx + console.log('Rename'), + text: 'Rename', + }, + { + action: () => console.log('Delete'), + text: 'Delete', + theme: 'danger', + }, + ]} +/> +``` + + + +## Сгруппированные элементы + +Элементы компонента`DropdownMenu` можно группировать и визуально отделять от остальных с помощью массивов элементов меню, вложенных в массив `items`. + + + + + +```jsx + console.log('Call'), + text: 'Call', + }, + { + action: () => console.log('Send email'), + text: 'Send email', + }, + ], + { + action: () => console.log('Rename'), + text: 'Rename', + }, + { + action: () => console.log('Delete'), + text: 'Delete', + theme: 'danger', + }, + ]} +/> +``` + + + +## Подменю + +Используя свойство `items` для отдельного элемента меню, можно добавить вложенные подэлементы. + +Для элементов меню с подменю предусмотрены следующие дополнительные классы для стилизации: + +- `.g-dropdown-menu__menu-item_with-submenu`— для элементов меню с более чем одним вложенным подэлементом; +- `.g-dropdown-menu__menu-item_active-parent`— для элемента, подменю которого в данный момент открыто. + + + + + +```jsx + console.log('Rename'), + text: 'Rename', + }, + { + action: () => console.log('Delete'), + text: 'Delete', + theme: 'danger', + }, + { + text: 'More', + items: [ + { + action: () => console.log('Mark as'), + text: 'Mark as', + items: [ + { + action: () => console.log('Important'), + text: 'Important', + }, + { + action: () => console.log('Favorite'), + text: 'Favorite', + }, + ], + }, + { + action: () => console.log('Copy'), + text: 'Copy', + }, + { + text: 'Move to', + items: [ + { + action: () => console.log('Location #1'), + text: 'Location #1', + }, + { + action: () => console.log('Location #2'), + text: 'Location #2', + }, + ], + }, + ], + }, + ]} +/> +``` + + + +## Пользовательский переключатель меню + +Для настройки переключателя меню используйте свойство `renderSwitcher`. Это может быть любая функция, возвращающая React-компонент (или `(props: SwitcherProps) => React.ReactNode` в контексте TypeScript; см. [`SwitcherProps`](#switcherprops) ниже). По умолчанию переключатель меню — кнопка с иконкой многоточия (**⋯**). + + + + + +```jsx + ( +
+ John Doe +
+ )} + items={[ + { + action: () => console.log('Rename'), + text: 'Rename', + }, + { + action: () => console.log('Delete'), + text: 'Delete', + theme: 'danger', + }, + ]} +/> +``` + + + +Пример выше упрощен с целью показать принцип работы настраиваемого переключателя меню. В реальных приложениях желательно, чтобы кликабельный переключатель меню представлял собой компонент, доступный для управления с клавиатуры и через другие вспомогательные технологии, такие как кнопка. + +## Пользовательские иконки + +Для добавления пользовательских иконок к элементам `DropdownMenu` используйте свойства `iconStart` и `iconEnd`. По умолчанию в элементах `DropdownMenu` иконки отсутствуют. + +Чтобы изменить иконку переключателя меню, используйте свойства `renderSwitcher` компонента`DropdownMenu` По умолчанию переключатель меню — кнопка с иконкой многоточия (**⋯**). + + + + + +```jsx + ( + + )} + items={[ + { + iconStart: , + action: () => console.log('Rename'), + text: 'Rename', + }, + { + iconStart: , + action: () => console.log('Delete'), + text: 'Delete', + theme: 'danger', + }, + ]} +/> +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-------------------: | +| `items` | Массив элементов. Вложенные массивы элементов представляют визуально разделенные группы. | `(DropdownMenuItem \| DropdownMenuItem[])[] \| []` | | +| `data` | Данные, которые передаются в действия, вызываемые из меню (это может быть полезно для контекстных меню). | `any` | | +| `icon` | Иконка дефолтного переключателя (`switcher`). | `React.ReactNode` | Иконка многоточия. | +| `size` | Применяется как к дефолтному `switcher`, так и к меню. | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` | +| `disabled` | Значение `true` для этого свойства отключает кнопку `switcher` и блокирует открытие меню. | `boolean` | | +| `renderSwitcher` | Функция рендеринга для контрола переключения меню. | `React.ReactNode` | | +| `switcherWrapperClassName` | Значение для свойства `className` родительского компонента `switcher`. | `string` | | +| `defaultSwitcherProps` | Свойства дефолтного `switcher`. | `ButtonProps` | | +| `defaultSwitcherClassName` | Значение для свойства `className` дефолтного `switcher`. | `string` | | +| `menuProps` | Переопределяет свойства выпадающего меню по умолчанию. | `MenuProps` | | +| `popupProps` | Переопределяет свойства всплывающего окна по умолчанию. | `PopupProps` | | +| `open` | Переключает видимость выпадающего меню. | `boolean` | | +| `onOpenToggle` | Вызывается при открытии или закрытии меню. | `() => void` | | +| `onSwitcherClick` | Вызывается при клике по переключателю. | `React.MouseEventHandler` | | +| `hideOnScroll` | Указывает, нужно ли скрывать меню при прокрутке родительского элемента. | `boolean` | `true` | +| `children` | Пользовательский контент внутри всплывающего окна с меню. | `React.ReactNode` | | + +### DropdownMenuItem + +Используется для описания отдельных элементов выпадающего меню. + +| Имя | Описание | Тип | Значение по умолчанию | +| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------: | :-------------------: | +| `text` | Текстовое содержимое элемента меню. | `React.ReactNode` | | +| `action` | Обработчик клика по элементу меню. Получает параметры от родительского выпадающего меню (`event` и `data`). | `(event: React.MouseEvent, data: any) => void` | | +| `iconStart` | Иконка, отображаемая перед содержимым элемента меню. | `React.ReactNode` | | +| `iconEnd` | Иконка, отображаемая после содержимого элемента меню. Игнорируется, если у элемента есть подменю. | `React.ReactNode` | | +| `hidden` | Определяет, скрыт ли элемент меню. | `boolean` | | +| `disabled` | Определяет, заблокирован ли элемент меню. | `boolean` | | +| `href` | Элемент меню с этим свойством становится ссылкой на указанное местоположение. | `string` | | +| `target` | То же, что и атрибут `target` у тега ``. | `string` | | +| `rel` | То же, что и атрибут `rel` у тега ``. | `string` | | +| `extraProps` | Дополнительные свойства для элемента меню. | `object` | | +| `title` | Текст всплывающей подсказки. | `string` | | +| `className` | Значение HTML-атрибута `class`. | `string` | | +| `items` | Элементы подменю. | `(DropdownMenuItem \| DropdownMenuItem[])[]` | | +| `popupProps` | Свойства всплывающего окна подменю. | `string` | | +| `path` | Путь индексов от корня до текущего элемента. | `number[]` | | +| `closeMenu` | Пользовательская функция для закрытия меню (`closeMenu`). Ее можно вызвать вместо закрытия основного меню. Позволяет сначала закрыть подменю, а затем основное меню. | `() => void` | | + +### SwitcherProps + +| Имя | Описание | Тип | +| :---------- | :------------------------------------------------------------------------- | :----------: | +| `onClick` | Вызывается при клике по переключателю. | `() => void` | +| `onKeyDown` | Вызывается при получении переключателем фокуса и нажатии клавиши действия. | `() => void` | diff --git a/src/components/DropdownMenu/README.md b/src/components/DropdownMenu/README.md index 9c178322a1..55167a1418 100644 --- a/src/components/DropdownMenu/README.md +++ b/src/components/DropdownMenu/README.md @@ -8,7 +8,7 @@ import {DropdownMenu} from '@gravity-ui/uikit'; ``` -This is a dropdown menu component with item grouping, submenus, and a customizable toggle. The dropdown menu items are configured with the `items` prop. By default, the menu toggle is a button with the ellipsis icon (**⋯**), which can be overridden with the `renderSwitcher` prop. +The dropdown menu component provides item grouping, submenus, and a customizable toggle. The dropdown menu items are configured with the `items` property. By default, the menu toggle is a button with the ellipsis icon (**⋯**), which can be overridden with the `renderSwitcher` property. ## Submenus -The `items` property on an individual menu item adds nested subitems to this menu item. +The `items` property on an individual menu item adds nested sub-items to such item. -Menu items with submenus obtain the following additional class names to allow for extra styling: +Menu items with submenus get the following additional class names to allow for extra styling: -- the class name `.g-dropdown-menu__menu-item_with-submenu` is added to items with more than 1 nested item; -- the class name `.g-dropdown-menu__menu-item_active-parent` is added to an item whose submenu is currently open. +- `.g-dropdown-menu__menu-item_with-submenu`: For items with more than one nested item. +- `.g-dropdown-menu__menu-item_active-parent`: For the item whose submenu is currently open. ## Custom menu toggle -The menu toggle is configured with the `renderSwitcher` prop. It can be any function that returns a React component (or any `(props: SwitcherProps) => React.ReactNode` in the TypeScript terms, see [`SwitcherProps`](#switcherprops) below). By default, the menu toggle is a button with the ellipsis icon (**⋯**). +To configure the menu toggle, use the `renderSwitcher` property. It can be any function that returns a React component (or any `(props: SwitcherProps) => React.ReactNode` in the TypeScript terms, see [`SwitcherProps`](#switcherprops) below). By default, the menu toggle is a button with the ellipsis icon (**⋯**). -The example above is oversimplified to demonstrate the idea of the customizable menu toggle. In a real-life application, it is generally recommended that the clickable menu toggle should be a component accessible with a keyboard and other assistive technologies (such as a button). +The example above is oversimplified to demonstrate the idea of the customizable menu toggle. In a real-life application, it is generally recommended that the clickable menu toggle should be a component accessible with a keyboard and other assistive technologies such as a button. ## Custom icons -Custom icons can be added to a `DropdownMenu` item by assigning the `iconStart` or `iconEnd` property. By default, `DropdownMenu` items go without icons. +You can add custom icons to a `DropdownMenu` item using the `iconStart` or `iconEnd` property. By default, the `DropdownMenu` items go without icons. -The menu toggle icon can be changed with the `DropdownMenu`'s `renderSwitcher` prop. By default, the menu toggle is a button with the ellipsis icon (**⋯**). +You can change the menu toggle icon with the `DropdownMenu`'s `renderSwitcher` properties. By default, the menu toggle is a button with the ellipsis icon (**⋯**). ## Properties -| Name | Description | Type | Default | -| :------------------------- | :--------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-----------: | -| `items` | Array of items. Nested arrays of items represent visually separated groups. | `(DropdownMenuItem \| DropdownMenuItem[])[] \| []` | | -| `data` | A payload passed to the actions called from the menu. (Can be useful for context menus.) | `any` | | -| `icon` | Icon of the default `switcher`. | `React.ReactNode` | Ellipsis icon | -| `size` | Applied both to the default `switcher` and the menu. | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` | -| `disabled` | Setting this prop to `true` disables the `switcher` button and prevents the menu from opening. | `boolean` | | -| `renderSwitcher` | Render function for the menu toggle control. | `React.ReactNode` | | -| `switcherWrapperClassName` | Value for the `className` prop of the `switcher`'s parent component. | `string` | | -| `defaultSwitcherProps` | Default `switcher` props. | `ButtonProps` | | -| `defaultSwitcherClassName` | Value for the `className` prop of the default `switcher`. | `string` | | -| `menuProps` | Overrides the default dropdown menu popup props. | `MenuProps` | | -| `popupProps` | Overrides the default popup props. | `PopupProps` | | -| `open` | Controls dropdown menu visibility. | `boolean` | | -| `onOpenToggle` | Called when the menu is opened or closed. | `() => void` | | -| `onSwitcherClick` | Called when `switcher` is clicked. | `React.MouseEventHandler` | | -| `hideOnScroll` | Specifies whether to hide the menu when a parent element is scrolled. | `boolean` | `true` | -| `children` | Custom content inside the menu popup. | `React.ReactNode` | | +| Name | Description | Type | Default | +| :------------------------- | :------------------------------------------------------------------------------------------------- | :------------------------------------------------: | :-----------: | +| `items` | Array of items. Nested arrays of items represent visually separated groups. | `(DropdownMenuItem \| DropdownMenuItem[])[] \| []` | | +| `data` | A payload provided to the actions called from the menu. (This can be useful for context menus.) | `any` | | +| `icon` | Icon of the default `switcher`. | `React.ReactNode` | Ellipsis icon | +| `size` | Applied both to the default `switcher` and the menu. | `'s' \| 'm' \| 'l' \| 'xl'` | `'m'` | +| `disabled` | Setting this property to `true` disables the `switcher` button and prevents the menu from opening. | `boolean` | | +| `renderSwitcher` | Render function for the menu toggle control. | `React.ReactNode` | | +| `switcherWrapperClassName` | Value for the `className` property of the `switcher`'s parent component. | `string` | | +| `defaultSwitcherProps` | Default `switcher` properties. | `ButtonProps` | | +| `defaultSwitcherClassName` | Value for the `className` property of the default `switcher`. | `string` | | +| `menuProps` | Overrides the default dropdown menu popup properties. | `MenuProps` | | +| `popupProps` | Overrides the default popup properties. | `PopupProps` | | +| `open` | Toggles dropdown menu visibility. | `boolean` | | +| `onOpenToggle` | Called when the menu is opened or closed. | `() => void` | | +| `onSwitcherClick` | Called when `switcher` is clicked. | `React.MouseEventHandler` | | +| `hideOnScroll` | Specifies whether to hide the menu when a parent element is scrolled. | `boolean` | `true` | +| `children` | Custom content inside the menu popup. | `React.ReactNode` | | ### DropdownMenuItem @@ -532,20 +532,20 @@ This type describes individual dropdown menu items. | Name | Description | Type | Default | | :----------- | :------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------: | :-----: | | `text` | Menu item content. | `React.ReactNode` | | -| `action` | Menu item click handler. Recieves the parameters from the parent dropdown menu component (both `event` and `data`). | `(event: React.MouseEvent, data: any) => void` | | +| `action` | Menu item click handler. It gets the parameters from the parent dropdown menu component (both `event` and `data`). | `(event: React.MouseEvent, data: any) => void` | | | `iconStart` | Menu item icon before the item content. | `React.ReactNode` | | | `iconEnd` | Menu item icon after the item content. Ignored if the item has a submenu. | `React.ReactNode` | | | `hidden` | Determines whether the item is hidden. | `boolean` | | | `disabled` | Determines whether the item is disabled. | `boolean` | | -| `href` | Menu item with this prop becomes a link to the specified location. | `string` | | +| `href` | Menu item with this property becomes a link to the specified location. | `string` | | | `target` | Same as the `target` attribute of the `` tag. | `string` | | | `rel` | Same as the `rel` attribute of the `` tag. | `string` | | -| `extraProps` | Additional menu item props. | `object` | | +| `extraProps` | Additional menu item properties. | `object` | | | `title` | Tooltip text. | `string` | | -| `className` | HTML `class` attribute value. | `string` | | +| `className` | `class` HTML attribute value. | `string` | | | `items` | Submenu items. | `(DropdownMenuItem \| DropdownMenuItem[])[]` | | -| `popupProps` | Submenu popup props. | `string` | | -| `path` | Path of indexes from the root to the current item. | `number[]` | | +| `popupProps` | Submenu popup properties. | `string` | | +| `path` | Path of the indexes from the root to the current item. | `number[]` | | | `closeMenu` | Custom `closeMenu` callback. It can be called instead of closing the main menu and used to close submenus before the main menu. | `() => void` | | ### SwitcherProps diff --git a/src/components/DropdownMenu/__stories__/DropdownMenu.stories.tsx b/src/components/DropdownMenu/__stories__/DropdownMenu.stories.tsx index ec720bb0eb..031166f159 100644 --- a/src/components/DropdownMenu/__stories__/DropdownMenu.stories.tsx +++ b/src/components/DropdownMenu/__stories__/DropdownMenu.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Bars} from '@gravity-ui/icons'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/DropdownMenu/__stories__/options.tsx b/src/components/DropdownMenu/__stories__/options.tsx index e5b07c23ad..19d438b905 100644 --- a/src/components/DropdownMenu/__stories__/options.tsx +++ b/src/components/DropdownMenu/__stories__/options.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {DropdownMenuItem, DropdownMenuItemMixed} from '../types'; diff --git a/src/components/DropdownMenu/hooks/usePopupVisibility.ts b/src/components/DropdownMenu/hooks/usePopupVisibility.ts index f133f42746..84c37471f7 100644 --- a/src/components/DropdownMenu/hooks/usePopupVisibility.ts +++ b/src/components/DropdownMenu/hooks/usePopupVisibility.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {useConditionallyControlledState} from '../../../hooks/private'; diff --git a/src/components/DropdownMenu/hooks/useScrollHandler.ts b/src/components/DropdownMenu/hooks/useScrollHandler.ts index 03bea1811a..a632a7f99f 100644 --- a/src/components/DropdownMenu/hooks/useScrollHandler.ts +++ b/src/components/DropdownMenu/hooks/useScrollHandler.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; export function useScrollHandler( onScroll: (event: Event) => void, diff --git a/src/components/DropdownMenu/hooks/useSubmenu.ts b/src/components/DropdownMenu/hooks/useSubmenu.ts index 03962242cb..ba09bce109 100644 --- a/src/components/DropdownMenu/hooks/useSubmenu.ts +++ b/src/components/DropdownMenu/hooks/useSubmenu.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {DropdownMenuNavigationContext} from '../DropdownMenuNavigationContext'; import type {DropdownMenuListItem} from '../types'; diff --git a/src/components/FilePreview/FilePreview.tsx b/src/components/FilePreview/FilePreview.tsx index 563387533a..755d47b126 100644 --- a/src/components/FilePreview/FilePreview.tsx +++ b/src/components/FilePreview/FilePreview.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import { FileZipper as ArchiveIcon, diff --git a/src/components/FilePreview/FilePreviewAction.tsx b/src/components/FilePreview/FilePreviewAction.tsx index be75401df5..7c0723455e 100644 --- a/src/components/FilePreview/FilePreviewAction.tsx +++ b/src/components/FilePreview/FilePreviewAction.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import {ActionTooltip} from '../ActionTooltip'; import type {ActionTooltipProps} from '../ActionTooltip'; diff --git a/src/components/FilePreview/MobileImagePreview/MobileImagePreview.tsx b/src/components/FilePreview/MobileImagePreview/MobileImagePreview.tsx index 76c837bf38..b01422bc21 100644 --- a/src/components/FilePreview/MobileImagePreview/MobileImagePreview.tsx +++ b/src/components/FilePreview/MobileImagePreview/MobileImagePreview.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {ArrowLeft as ArrowLeftIcon} from '@gravity-ui/icons'; diff --git a/src/components/FilePreview/__stories__/FilePreview.stories.tsx b/src/components/FilePreview/__stories__/FilePreview.stories.tsx index bd2caf7c19..8e6bf9ba32 100644 --- a/src/components/FilePreview/__stories__/FilePreview.stories.tsx +++ b/src/components/FilePreview/__stories__/FilePreview.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {CircleExclamation, Link, Xmark} from '@gravity-ui/icons'; import {action} from '@storybook/addon-actions'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/FilePreview/__tests__/FilePreview.test.tsx b/src/components/FilePreview/__tests__/FilePreview.test.tsx index fba4915c52..27f4a09a61 100644 --- a/src/components/FilePreview/__tests__/FilePreview.test.tsx +++ b/src/components/FilePreview/__tests__/FilePreview.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {CircleExclamation} from '@gravity-ui/icons'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/HelpMark/HelpMark.tsx b/src/components/HelpMark/HelpMark.tsx index 2dd4017167..730f29397a 100644 --- a/src/components/HelpMark/HelpMark.tsx +++ b/src/components/HelpMark/HelpMark.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import {CircleQuestion} from '@gravity-ui/icons'; diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-dark-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-dark-chromium-linux.png new file mode 100644 index 0000000000..8b03de8d6b Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-dark-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-light-chromium-linux.png new file mode 100644 index 0000000000..8428ad5299 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-render-story-Default-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-default-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-default-light-chromium-linux.png new file mode 100644 index 0000000000..b499f0d850 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-default-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-end-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-end-light-chromium-linux.png new file mode 100644 index 0000000000..ef4a500fc1 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-end-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-light-chromium-linux.png new file mode 100644 index 0000000000..b2e6863a50 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-start-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-start-light-chromium-linux.png new file mode 100644 index 0000000000..72e3180858 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-auto-start-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-end-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-end-light-chromium-linux.png new file mode 100644 index 0000000000..148734a7a8 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-end-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-light-chromium-linux.png new file mode 100644 index 0000000000..fa7ba4112b Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-start-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-start-light-chromium-linux.png new file mode 100644 index 0000000000..3229e3825a Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-bottom-start-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-end-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-end-light-chromium-linux.png new file mode 100644 index 0000000000..9359becdfc Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-end-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-light-chromium-linux.png new file mode 100644 index 0000000000..481b1e1236 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-start-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-start-light-chromium-linux.png new file mode 100644 index 0000000000..9e5b709a5c Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-left-start-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-end-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-end-light-chromium-linux.png new file mode 100644 index 0000000000..ec7648c255 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-end-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-light-chromium-linux.png new file mode 100644 index 0000000000..cae9a146a5 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-start-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-start-light-chromium-linux.png new file mode 100644 index 0000000000..83aca38962 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-right-start-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-end-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-end-light-chromium-linux.png new file mode 100644 index 0000000000..d63b009509 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-end-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-light-chromium-linux.png new file mode 100644 index 0000000000..0b4e02203f Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-start-light-chromium-linux.png b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-start-light-chromium-linux.png new file mode 100644 index 0000000000..9a19de79f6 Binary files /dev/null and b/src/components/HelpMark/__snapshots__/HelpMark.visual.test.tsx-snapshots/HelpMark-smoke-placement-top-start-light-chromium-linux.png differ diff --git a/src/components/HelpMark/__tests__/HelpMark.test.tsx b/src/components/HelpMark/__tests__/HelpMark.test.tsx index 00e451fa3a..5cabf755c3 100644 --- a/src/components/HelpMark/__tests__/HelpMark.test.tsx +++ b/src/components/HelpMark/__tests__/HelpMark.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import userEvent from '@testing-library/user-event'; import {setupTimersMock} from '../../../../test-utils/setupTimersMock'; diff --git a/src/components/HelpMark/__tests__/HelpMark.visual.test.tsx b/src/components/HelpMark/__tests__/HelpMark.visual.test.tsx new file mode 100644 index 0000000000..e3c2c5b286 --- /dev/null +++ b/src/components/HelpMark/__tests__/HelpMark.visual.test.tsx @@ -0,0 +1,52 @@ +import {expect} from '@playwright/experimental-ct-react'; + +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {HelpMarkProps} from '../HelpMark'; +import {HelpMark} from '../HelpMark'; + +import {placementCases} from './cases'; +import {HelpMarkStories} from './stories'; + +test.describe('HelpMark', {tag: '@HelpMark'}, () => { + test('render story: ', async ({mount, expectScreenshot}) => { + await mount(); + + await expectScreenshot(); + }); + + createSmokeScenarios( + {}, + { + placement: placementCases, + }, + ).forEach(([title, props]) => { + smokeTest(title, async ({mount, page, expectScreenshot}) => { + const root = await mount( +
+

{title}

+
+ ' + 'data-qa': 'trigger', + }} + {...props} + > + Test content + +
+
, + ); + + await root.getByTestId('trigger').hover(); + await expect(page.getByTestId('popover-tooltip')).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + }); +}); diff --git a/src/components/HelpMark/__tests__/cases.ts b/src/components/HelpMark/__tests__/cases.ts new file mode 100644 index 0000000000..785090adac --- /dev/null +++ b/src/components/HelpMark/__tests__/cases.ts @@ -0,0 +1,20 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {HelpMarkProps} from '../HelpMark'; + +export const placementCases: Cases = [ + 'auto', + 'auto-start', + 'auto-end', + 'top', + 'bottom', + 'right', + 'left', + 'top-start', + 'top-end', + 'bottom-start', + 'bottom-end', + 'right-start', + 'right-end', + 'left-start', + 'left-end', +]; diff --git a/src/components/HelpMark/__tests__/stories.ts b/src/components/HelpMark/__tests__/stories.ts new file mode 100644 index 0000000000..50e261cacc --- /dev/null +++ b/src/components/HelpMark/__tests__/stories.ts @@ -0,0 +1,5 @@ +import {composeStories} from '@storybook/react'; + +import * as CSFStories from '../__stories__/HelpMark.stories'; + +export const HelpMarkStories = composeStories(CSFStories); diff --git a/src/components/Hotkey/Hotkey.tsx b/src/components/Hotkey/Hotkey.tsx index b15f6a945f..333e982a60 100644 --- a/src/components/Hotkey/Hotkey.tsx +++ b/src/components/Hotkey/Hotkey.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import type {AriaLabelingProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; diff --git a/src/components/Hotkey/README-ru.md b/src/components/Hotkey/README-ru.md new file mode 100644 index 0000000000..be1c8c4ef5 --- /dev/null +++ b/src/components/Hotkey/README-ru.md @@ -0,0 +1,88 @@ + + +# Hotkey + + + +```tsx +import {Hotkey} from '@gravity-ui/uikit'; +``` + +Компонент `Hotkey` (горячая клавиша) позволяет отображать сочетания клавиш как для Mac, так и для PC. + +### Значение + +Сочетания клавиш задаются в формате `+`, т. е. несколько клавиш, разделенных знаком плюса, например, `shift+tab`. + +Последовательности сочетаний клавиш могут быть разделены пробелом: ` `, например, `ctrl+a ctrl+c ctrl+v`. + +В качестве заменителя `cmd` на Mac и `ctrl` на других платформах можно использовать `mod`. Например, `mod+v` отображается как ⌘+A на Mac и как Ctrl+A на PC. + +### `View` (вид) + +`light` — используется для отображения на светлом фоне (по умолчанию). + +`dark` — используется для отображения на темном фоне. + + + + + +``` + + +``` + + + +### `Platform` (платформа) + +`pc` — используется для отображения горячих клавиш для клавиатуры стандартного PC. + +`mac` — используется для отображения горячих клавиш для клавиатуры Macintosh. + +По умолчанию система автоматически определяет платформу. + + + + + +``` + + + +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :---------------------------------------------------------------------- | :-------------------: | :-------------------------: | +| view | Задает цветовую схему. | `"light"` `"dark"` | `"light"` | +| platform | Определяет платформу (PC или Macintosh) для отображения горячих клавиш. | `"pc"` `"mac"` | Определяется автоматически. | +| title | Значение горячих клавиш. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | Имя класса алерта. | `string` | | diff --git a/src/components/Hotkey/README.md b/src/components/Hotkey/README.md index 9c662a7b02..03cae9d7b9 100644 --- a/src/components/Hotkey/README.md +++ b/src/components/Hotkey/README.md @@ -8,21 +8,21 @@ import {Hotkey} from '@gravity-ui/uikit'; ``` -`Hotkey` component can be used for display keyboard shortcuts for different platforms: Mac or PC. +You can use the `Hotkey` component to display keyboard shortcuts for both Mac and PC. ### Value -Keyboard shortcuts are set in the format `+` – specify a set of keys separated by a plus. Example: `shift+tab`. +Keyboard shortcuts are set in the `+` format, which means you need to specify multiple keys separated by a plus sign, e.g., `shift+tab`. -The sequence of key combinations can be separated by a space: ` `. Example: `ctrl+a ctrl+c ctrl+v`. +The sequence of key combinations can be separated by a space: ` `, e.g., `ctrl+a ctrl+c ctrl+v`. -You can use `mod` as a shorthand for `cmd` on Mac and `ctrl` on other platforms. Example: `mod+v` is rendered as ⌘+A for Mac, and as Ctrl+A for PC. +You can use `mod` as a shorthand for `cmd` on Mac and `ctrl` for other platforms. For example, `mod+v` is rendered as ⌘+A for Mac and Ctrl+A for PC. ### View -`light` - used if the component is drawn on a light background (used by default). +`light`: Used if the component is displayed on a light background (used by default). -`dark` - used if the component is drawn on a dark background. +`dark`: Used if the component is displayed on a dark background. ### Platform -`pc` - used to display keyboard shortcuts for a regular PC keyboard. +`pc`: Used to display keyboard shortcuts for a regular PC keyboard. -`mac` - used to display keyboard shortcuts for a Macintosh keyboard. +`mac`: Used to display keyboard shortcuts for a Macintosh keyboard. By default, the platform is detected automatically. @@ -83,6 +83,6 @@ LANDING_BLOCK--> | :-------- | :----------------------------------------------------------------------------- | :-------------------: | :--------------------: | | view | Used to set the color scheme | `"light"` `"dark"` | `"light"` | | platform | Used to select the platform (PC or Macintosh) to display the keyboard shortcut | `"pc"` `"mac"` | Detected automatically | -| title | The value of the keyboard shortcut | `string` | | +| title | Keyboard shortcut value | `string` | | | style | HTML style attribute | `React.CSSProperties` | | -| className | The alert class name | `string` | | +| className | Alert class name | `string` | | diff --git a/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-dark-view-dark-chromium-linux.png b/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-dark-view-dark-chromium-linux.png new file mode 100644 index 0000000000..0eb269edce Binary files /dev/null and b/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-dark-view-dark-chromium-linux.png differ diff --git a/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-light-view-light-chromium-linux.png b/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-light-view-light-chromium-linux.png new file mode 100644 index 0000000000..d6f7313f7d Binary files /dev/null and b/src/components/Hotkey/__snapshots__/Hotkey.visual.test.tsx-snapshots/Hotkey-smoke-smoke-light-view-light-chromium-linux.png differ diff --git a/src/components/Hotkey/__stories__/Hotkey.stories.tsx b/src/components/Hotkey/__stories__/Hotkey.stories.tsx index d97b94a431..6416d3fa9b 100644 --- a/src/components/Hotkey/__stories__/Hotkey.stories.tsx +++ b/src/components/Hotkey/__stories__/Hotkey.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryFn} from '@storybook/react'; import {Table} from '../../Table'; diff --git a/src/components/Hotkey/__stories__/README.md b/src/components/Hotkey/__stories__/README.md new file mode 100644 index 0000000000..03cae9d7b9 --- /dev/null +++ b/src/components/Hotkey/__stories__/README.md @@ -0,0 +1,88 @@ + + +# Hotkey + + + +```tsx +import {Hotkey} from '@gravity-ui/uikit'; +``` + +You can use the `Hotkey` component to display keyboard shortcuts for both Mac and PC. + +### Value + +Keyboard shortcuts are set in the `+` format, which means you need to specify multiple keys separated by a plus sign, e.g., `shift+tab`. + +The sequence of key combinations can be separated by a space: ` `, e.g., `ctrl+a ctrl+c ctrl+v`. + +You can use `mod` as a shorthand for `cmd` on Mac and `ctrl` for other platforms. For example, `mod+v` is rendered as ⌘+A for Mac and Ctrl+A for PC. + +### View + +`light`: Used if the component is displayed on a light background (used by default). + +`dark`: Used if the component is displayed on a dark background. + + + + + +``` + + +``` + + + +### Platform + +`pc`: Used to display keyboard shortcuts for a regular PC keyboard. + +`mac`: Used to display keyboard shortcuts for a Macintosh keyboard. + +By default, the platform is detected automatically. + + + + + +``` + + + +``` + + + +## Properties + +| Name | Description | Type | Default | +| :-------- | :----------------------------------------------------------------------------- | :-------------------: | :--------------------: | +| view | Used to set the color scheme | `"light"` `"dark"` | `"light"` | +| platform | Used to select the platform (PC or Macintosh) to display the keyboard shortcut | `"pc"` `"mac"` | Detected automatically | +| title | Keyboard shortcut value | `string` | | +| style | HTML style attribute | `React.CSSProperties` | | +| className | Alert class name | `string` | | diff --git a/src/components/Hotkey/__tests__/Hotkey.visual.test.tsx b/src/components/Hotkey/__tests__/Hotkey.visual.test.tsx new file mode 100644 index 0000000000..2df190c557 --- /dev/null +++ b/src/components/Hotkey/__tests__/Hotkey.visual.test.tsx @@ -0,0 +1,67 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {HotkeyProps} from '../Hotkey'; +import {Hotkey} from '../Hotkey'; + +import {platformCases, valueCases} from './cases'; + +test.describe('Hotkey', {tag: '@Hotkey'}, () => { + smokeTest('smoke, light view', async ({mount, expectScreenshot}) => { + const defaultProps: HotkeyProps = { + value: 'mod+a', + view: 'light', + }; + + const smokeScenarios = createSmokeScenarios(defaultProps, { + value: valueCases, + platform: platformCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('smoke, dark view', async ({mount, expectScreenshot}) => { + const defaultProps: HotkeyProps = { + value: 'mod+a', + view: 'dark', + }; + + const smokeScenarios = createSmokeScenarios(defaultProps, { + value: valueCases, + platform: platformCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['dark'], + }); + }); +}); diff --git a/src/components/Hotkey/__tests__/cases.tsx b/src/components/Hotkey/__tests__/cases.tsx new file mode 100644 index 0000000000..e10d94fc39 --- /dev/null +++ b/src/components/Hotkey/__tests__/cases.tsx @@ -0,0 +1,10 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {HotkeyProps} from '../Hotkey'; + +export const valueCases: Cases = [ + 'mod+a', + 'mod+a mod+c', + 'mod+a mod+c mod+v', +]; + +export const platformCases: Cases = ['pc', 'mac']; diff --git a/src/components/Icon/Icon.scss b/src/components/Icon/Icon.scss index edc60f0841..533e12a5c0 100644 --- a/src/components/Icon/Icon.scss +++ b/src/components/Icon/Icon.scss @@ -3,7 +3,6 @@ $block: '.#{variables.$ns}icon'; #{$block} { - color: inherit; line-height: 0; vertical-align: top; } diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx index 0bc4b73419..0d245e3a1b 100644 --- a/src/components/Icon/Icon.tsx +++ b/src/components/Icon/Icon.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {QAProps} from '../types'; import {block} from '../utils/cn'; diff --git a/src/components/Icon/README-ru.md b/src/components/Icon/README-ru.md new file mode 100644 index 0000000000..a582ce86f2 --- /dev/null +++ b/src/components/Icon/README-ru.md @@ -0,0 +1,62 @@ + + +# Icon + + + +```tsx +import {Icon} from '@gravity-ui/uikit'; +``` + +Компонент `Icon` (иконка) представляет собой обертку для SVG-иконок. SVG-файлы можно загружать разными способами — например, через компоненты React или с использованием различных загрузчиков Webpack: [`SVGR`](https://react-svgr.com/docs/webpack/), [`svg-react-loader`](https://github.com/jhamlet/svg-react-loader), [`svg-inline-loader`](https://github.com/webpack-contrib/svg-inline-loader) или [`svg-sprite-loader`](https://github.com/JetBrains/svg-sprite-loader). +Компонент `Icon` действует как прокси для доступа к иконкам в базе кода. + +### Компонент React + +```tsx +// CheckIcon.jsx +export function CheckIcon() { + return ( + + + + ); +} + +// --- +import {CheckIcon} from './CheckIcon'; + +; +``` + +### Загрузчик Webpack + +```tsx +// webpack.config.js +{ + test: /\.svg$/, + use: [''], +} + +// check.svg + + + + +// --- +import CheckIcon from './check.svg'; + +; +``` + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :--------------------------------------------- | :---------------: | :-------------------: | +| data | Источник SVG-иконки. | `IconData` | | +| width | Атрибут `width` для SVG. | `number` `string` | | +| height | Атрибут `height` для SVG. | `number` `string` | | +| size | Атрибуты `width` и `height` для SVG. | `number` `string` | | +| fill | Атрибут `fill` для SVG. | `string` | `"currentColor"` | +| stroke | Атрибут `stroke` для SVG. | `string` | `"none"` | +| className | Пользовательский CSS-класс корневого элемента. | `string` | | diff --git a/src/components/Icon/README.md b/src/components/Icon/README.md index 0e636703aa..2a624b91eb 100644 --- a/src/components/Icon/README.md +++ b/src/components/Icon/README.md @@ -8,9 +8,7 @@ import {Icon} from '@gravity-ui/uikit'; ``` -The `Icon` component is a wrapper for SVG icon. SVGs can be loaded in different ways, -such as though a React component or various Webpack -loaders: [`SVGR`](https://react-svgr.com/docs/webpack/), [`svg-react-loader`](https://github.com/jhamlet/svg-react-loader), [`svg-inline-loader`](https://github.com/webpack-contrib/svg-inline-loader), or [`svg-sprite-loader`](https://github.com/JetBrains/svg-sprite-loader). +The `Icon` component is a wrapper for SVG icon. SVGs can be loaded in different ways, such as though a React component or various Webpack loaders: [`SVGR`](https://react-svgr.com/docs/webpack/), [`svg-react-loader`](https://github.com/jhamlet/svg-react-loader), [`svg-inline-loader`](https://github.com/webpack-contrib/svg-inline-loader), or [`svg-sprite-loader`](https://github.com/JetBrains/svg-sprite-loader). The `Icon` component serves as a proxy to use through the codebase. ### React component @@ -53,12 +51,12 @@ import CheckIcon from './check.svg'; ## Properties -| Name | Description | Type | Default | -| :-------- | :-------------------------------------- | :---------------: | :--------------: | -| data | Source of SVG icon | `IconData` | | -| width | `width` SVG attribute | `number` `string` | | -| height | `height` SVG attribute | `number` `string` | | -| size | Both `width` and `height` SVG attribute | `number` `string` | | -| fill | `fill` SVG attribute | `string` | `"currentColor"` | -| stroke | `stroke` SVG attribute | `string` | `"none"` | -| className | Custom CSS class for the root element | `string` | | +| Name | Description | Type | Default | +| :-------- | :--------------------------------------- | :---------------: | :--------------: | +| data | Source of SVG icon | `IconData` | | +| width | `width` SVG attribute | `number` `string` | | +| height | `height` SVG attribute | `number` `string` | | +| size | Both `width` and `height` SVG attributes | `number` `string` | | +| fill | `fill` SVG attribute | `string` | `"currentColor"` | +| stroke | `stroke` SVG attribute | `string` | `"none"` | +| className | Custom CSS class for the root element | `string` | | diff --git a/src/components/Icon/__snapshots__/Icon.visual.test.tsx-snapshots/Icon-smoke-smoke-light-chromium-linux.png b/src/components/Icon/__snapshots__/Icon.visual.test.tsx-snapshots/Icon-smoke-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..c2fee1f671 Binary files /dev/null and b/src/components/Icon/__snapshots__/Icon.visual.test.tsx-snapshots/Icon-smoke-smoke-light-chromium-linux.png differ diff --git a/src/components/Icon/__stories__/Icon.stories.tsx b/src/components/Icon/__stories__/Icon.stories.tsx index d292cbddda..4d77599352 100644 --- a/src/components/Icon/__stories__/Icon.stories.tsx +++ b/src/components/Icon/__stories__/Icon.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Envelope, Gear, Rocket} from '@gravity-ui/icons'; import type {Meta, StoryObj} from '@storybook/react'; diff --git a/src/components/Icon/__tests__/Icon.visual.test.tsx b/src/components/Icon/__tests__/Icon.visual.test.tsx new file mode 100644 index 0000000000..1f6ad5e06e --- /dev/null +++ b/src/components/Icon/__tests__/Icon.visual.test.tsx @@ -0,0 +1,35 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {IconProps} from '../Icon'; + +import {sizeCases} from './cases'; +import {TestIcon} from './helpersPlaywright'; + +test.describe('Icon', {tag: '@Icon'}, () => { + smokeTest('smoke', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios>( + {}, + { + size: sizeCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); +}); diff --git a/src/components/Icon/__tests__/cases.tsx b/src/components/Icon/__tests__/cases.tsx new file mode 100644 index 0000000000..744c0f3f03 --- /dev/null +++ b/src/components/Icon/__tests__/cases.tsx @@ -0,0 +1,4 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {IconProps} from '../Icon'; + +export const sizeCases: Cases = [10, 20, 30]; diff --git a/src/components/Icon/__tests__/helpersPlaywright.tsx b/src/components/Icon/__tests__/helpersPlaywright.tsx new file mode 100644 index 0000000000..c26bb84f32 --- /dev/null +++ b/src/components/Icon/__tests__/helpersPlaywright.tsx @@ -0,0 +1,8 @@ +import {Rocket} from '@gravity-ui/icons'; + +import type {IconProps} from '../Icon'; +import {Icon} from '../Icon'; + +export const TestIcon = (props: Omit) => { + return ; +}; diff --git a/src/components/Icon/types.ts b/src/components/Icon/types.ts index fe82642840..3d6bf78995 100644 --- a/src/components/Icon/types.ts +++ b/src/components/Icon/types.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; export interface SVGIconSpriteData { id: string; diff --git a/src/components/Label/Label.tsx b/src/components/Label/Label.tsx index f217390805..b960324a48 100644 --- a/src/components/Label/Label.tsx +++ b/src/components/Label/Label.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Xmark} from '@gravity-ui/icons'; @@ -11,6 +11,8 @@ import {Icon} from '../Icon'; import type {QAProps} from '../types'; import {block} from '../utils/cn'; +import {LabelQa} from './constants'; + import './Label.scss'; const b = block('label'); @@ -122,6 +124,7 @@ export const Label = React.forwardRef(function Label( side: 'end', type: 'button', })} + data-qa={LabelQa.copyButton} > @@ -137,6 +140,7 @@ export const Label = React.forwardRef(function Label( side: 'end', type: 'button', })} + data-qa={LabelQa.closeButton} > @@ -165,6 +169,7 @@ export const Label = React.forwardRef(function Label( type="button" onClick={onClick} className={b('main-button')} + data-qa={LabelQa.mainButton} > {content} diff --git a/src/components/Label/README-ru.md b/src/components/Label/README-ru.md new file mode 100644 index 0000000000..8f41a59895 --- /dev/null +++ b/src/components/Label/README-ru.md @@ -0,0 +1,272 @@ + + +# Label + + + +```tsx +import {Label} from '@gravity-ui/uikit'; +``` + +`Label` (лейбл) можно использовать для выделения определенной информации. `Label` с кнопкой `Close` или `Copy` может быть полезен для выполнения различных простых действий. + +Лейблы больше всего подходят для отображения однострочной текстовой информации с различными цветовыми кодами, показывающими ее важность. + +## Внешний вид + +`Label` можно отображать в различных стилях. + +### `Theme` (тема) + +Свойство `theme` позволяет применять различные темы в зависимости от статуса. Возможные значения: `normal`, `info`, `success`, `warning`, `danger`, `utility`, `unknown` и `clear`. +Тема по умолчанию — `normal`. + + + + + +```tsx + + + + + + + + +``` + + + +### `Type` (тип) + +Свойство `type` добавляет различные опции к `Label`: + +`copy` — добавляет кнопку копирования, при нажатии на которую копируется значение, указанное в свойстве `copyText`; + +`close` — добавляет кнопку закрытия для управления списком лейблов. + + + + + +```tsx + + + +``` + + + +### Иконка + +Иконку можно добавить с помощью свойства `icon`. Для этого используйте компонент [`Icon`](../Icon), который представляет собой обертку для SVG-файлов. + + + + + +```tsx + + + +``` + + + +## Значение + +`Label` можно применять для отображения информации в формате `ключ-значение`. Для этого необходимо передать ключ в свойство `children`, а значение — в свойство `value`. + + + + + +```tsx + + + + + + + + +``` + + + +## Состояние + +`Label` может иметь разные состояния: + +- `disabled` — взаимодействие с лейблом запрещено. +- `interactive` — лейбл становится интерактивным по ховеру. + + + + + +```tsx + + + +``` + + + +## Размер + + + + + +```tsx + + + +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :--------------- | :----------------------------------------------------- | :----------------------------: | :-------------------: | +| children | Содержимое. | `React.ReactNode` | | +| className | HTML-атрибут `class`. | `string` | | +| closeButtonLabel | `aria-label` кнопки закрытия. | `string` | | +| copyButtonLabel | `aria-label` кнопки копирования. | `string` | | +| copyText | Копируемый текст. | `string` | | +| disabled | Отключенное состояние. | `boolean` | | +| icon | Иконка лейбла (слева). | `React.ReactNode` | | +| interactive | Включение эффекта ховера. | `boolean` | | +| onClick | Обработчик события `click`. | `Function` | | +| onCloseClick | Обработчик события `click` по кнопке закрытия. | `Function` | | +| onCopy | Обработчик события `copy`. | `Function` | | +| size | Размер лейбла. | `"xs"` `"s"` `"m"` | `"s"` | +| theme | Тема лейбла. | `string` | `"normal"` | +| type | Тип лейбла. | `"default"` `"copy"` `"close"` | `"default"` | +| value | Значение лейбла (в виде `"children : value"`). | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | diff --git a/src/components/Label/README.md b/src/components/Label/README.md index f1d68832c7..d39d5973cc 100644 --- a/src/components/Label/README.md +++ b/src/components/Label/README.md @@ -8,17 +8,17 @@ import {Label} from '@gravity-ui/uikit'; ``` -`Label` can be used for displaying certain marking information. `Label` with the close or copy button may be useful for various simple actions. +You can use `Label`s for highlighting certain information. A `Label` with the `Close` or `Copy` button may be useful for various simple actions. -`Label` is most suitable for displaying one-line text information with different color codes indicating its importance. +`Label`s are most suitable for displaying one-line text information with different color codes that show its importance. ## Appearance -`Label` can be displayed in multiple styles. +A `Label` can be displayed in multiple styles. ### Theme -Apply different themes for various statuses with the `theme` property. You can use the following values: `normal`, `info`, `success`, `warning`, `danger`, `utility`, `unknown`, and `clear`. +Use the `theme` property to apply different themes for various statuses. You can use the following values: `normal`, `info`, `success`, `warning`, `danger`, `utility`, `unknown`, and `clear`. The default theme is `normal`. ### Type -Adds various options to the `Label` using the `type` property. +The `type` property adds various options to a `Label`: `copy`: Adds a copy button; when clicked, it copies the value of the `copyText` property. @@ -97,7 +97,7 @@ LANDING_BLOCK--> ### Icon -You can add an icon with the `icon` property. To do so, use the [`Icon`](../Icon) component, a special wrapper for SVGs. +You can add an icon with the `icon` property. To do so, use the [`Icon`](../Icon) component, which is a special wrapper for SVGs. ## Value -`Label` can be used for displaying key-value information. Pass key to the `children` and value to the `value` property. +You can use `Label`s for displaying key-value information. For that, you need to provide the key to the `children` poperty, and value, to `value`: ## Properties -| Name | Description | Type | Default | -| :--------------- | :-------------------------------------------- | :----------------------------: | :---------: | -| children | Content | `React.ReactNode` | | -| className | HTML `class` attribute | `string` | | -| closeButtonLabel | `aria-label` of the close button | `string` | | -| copyButtonLabel | `aria-label` of the copy button | `string` | | -| copyText | Text to copy | `string` | | -| disabled | Disabled state | `boolean` | | -| icon | Label icon (on the left) | `React.ReactNode` | | -| interactive | Enable hover effect | `boolean` | | -| onClick | `click` event handler | `Function` | | -| onCloseClick | Close button `click` event handler | `Function` | | -| onCopy | `copy` event handler | `Function` | | -| size | Label size | `"xs"` `"s"` `"m"` | `"s"` | -| theme | Label theme | `string` | `"normal"` | -| type | Label type | `"default"` `"copy"` `"close"` | `"default"` | -| value | Label value (displayed as "children : value") | `string` | | -| title | HTML `title` attribute | `string` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :--------------- | :---------------------------------------------- | :----------------------------: | :---------: | +| children | Content | `React.ReactNode` | | +| className | `class` HTML attribute | `string` | | +| closeButtonLabel | `aria-label` of the close button | `string` | | +| copyButtonLabel | `aria-label` of the copy button | `string` | | +| copyText | Text to copy | `string` | | +| disabled | Disabled state | `boolean` | | +| icon | Label icon (on the left) | `React.ReactNode` | | +| interactive | Enables hover effect | `boolean` | | +| onClick | `click` event handler | `Function` | | +| onCloseClick | Close button `click` event handler | `Function` | | +| onCopy | `copy` event handler | `Function` | | +| size | Label size | `"xs"` `"s"` `"m"` | `"s"` | +| theme | Label theme | `string` | `"normal"` | +| type | Label type | `"default"` `"copy"` `"close"` | `"default"` | +| value | Label value (displayed as `"children : value"`) | `string` | | +| title | `title` HTML attribute | `string` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-chromium-linux.png deleted file mode 100644 index 3280efe254..0000000000 Binary files a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-chromium-linux.png and /dev/null differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-webkit-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-webkit-linux.png deleted file mode 100644 index 2a9a7f0257..0000000000 Binary files a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-chromium-linux.png deleted file mode 100644 index ea47fb49db..0000000000 Binary files a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-chromium-linux.png and /dev/null differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-webkit-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-webkit-linux.png deleted file mode 100644 index 1a38c3a13e..0000000000 Binary files a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-render-story-ShowcaseStory-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-dark-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-dark-chromium-linux.png new file mode 100644 index 0000000000..9ce4aeeff0 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-dark-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-light-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..4a4f495fad Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-light-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-dark-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-dark-chromium-linux.png new file mode 100644 index 0000000000..c66994e395 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-dark-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-light-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-light-chromium-linux.png new file mode 100644 index 0000000000..1ed4e1de41 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-close-light-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-dark-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-dark-chromium-linux.png new file mode 100644 index 0000000000..f6d63e809a Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-dark-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-light-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-light-chromium-linux.png new file mode 100644 index 0000000000..bf4a743393 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-copy-light-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-dark-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-dark-chromium-linux.png new file mode 100644 index 0000000000..de088cccb4 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-dark-chromium-linux.png differ diff --git a/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-light-chromium-linux.png b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-light-chromium-linux.png new file mode 100644 index 0000000000..8214a6b7f8 Binary files /dev/null and b/src/components/Label/__snapshots__/Label.visual.test.tsx-snapshots/Label-smoke-with-custom-icon-light-chromium-linux.png differ diff --git a/src/components/Label/__stories__/Label.stories.tsx b/src/components/Label/__stories__/Label.stories.tsx index aac0960180..201d8f170f 100644 --- a/src/components/Label/__stories__/Label.stories.tsx +++ b/src/components/Label/__stories__/Label.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Check} from '@gravity-ui/icons'; import type {Meta, StoryObj} from '@storybook/react'; diff --git a/src/components/Label/__stories__/LabelShowcase.tsx b/src/components/Label/__stories__/LabelShowcase.tsx index 838ac5a8ca..4c5b03e896 100644 --- a/src/components/Label/__stories__/LabelShowcase.tsx +++ b/src/components/Label/__stories__/LabelShowcase.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Check, Gear} from '@gravity-ui/icons'; diff --git a/src/components/Label/__tests__/Label.visual.test.tsx b/src/components/Label/__tests__/Label.visual.test.tsx index b13df3ea33..2570d1be0d 100644 --- a/src/components/Label/__tests__/Label.visual.test.tsx +++ b/src/components/Label/__tests__/Label.visual.test.tsx @@ -1,10 +1,15 @@ -import React from 'react'; +import {smokeTest, test} from '~playwright/core'; -import {test} from '~playwright/core'; +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {LabelProps} from '../Label'; +import {Label} from '../Label'; -import {LabelStories} from './helpersPlaywright'; +import {disabledCases, sizeCases, themeCases} from './cases'; +import {LabelStories, TestLabelWithIcon} from './helpersPlaywright'; -test.describe('Label', () => { +const qa = 'label'; + +test.describe('Label', {tag: '@Label'}, () => { test('render story: ', async ({mount, expectScreenshot}) => { await mount(); @@ -64,4 +69,117 @@ test.describe('Label', () => { await expectScreenshot(); }); + + const defaultProps: LabelProps = { + children: 'Label', + qa, + type: 'default', + }; + + smokeTest('', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios(defaultProps, { + theme: themeCases, + size: sizeCases, + disabled: disabledCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('with custom icon', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios(defaultProps, { + theme: themeCases, + size: sizeCases, + disabled: disabledCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('with copy', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + type: 'copy', + copyText: 'Copy text', + onCopy: () => {}, + }, + { + theme: themeCases, + size: sizeCases, + disabled: disabledCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('with close', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + type: 'close', + onCloseClick: () => {}, + }, + { + theme: themeCases, + size: sizeCases, + disabled: disabledCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); }); diff --git a/src/components/Label/__tests__/cases.tsx b/src/components/Label/__tests__/cases.tsx new file mode 100644 index 0000000000..f13b984e44 --- /dev/null +++ b/src/components/Label/__tests__/cases.tsx @@ -0,0 +1,15 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {LabelProps} from '../Label'; + +export const disabledCases: Cases = [true]; +export const themeCases: Cases = [ + 'normal', + 'info', + 'danger', + 'warning', + 'success', + 'utility', + 'unknown', + 'clear', +]; +export const sizeCases: Cases = ['xs', 's', 'm']; diff --git a/src/components/Label/__tests__/helpersPlaywright.ts b/src/components/Label/__tests__/helpersPlaywright.ts deleted file mode 100644 index 101a11f4b9..0000000000 --- a/src/components/Label/__tests__/helpersPlaywright.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {composeStories} from '@storybook/react'; - -import * as DefaultLabelStories from '../__stories__/Label.stories'; - -export const LabelStories = composeStories(DefaultLabelStories); diff --git a/src/components/Label/__tests__/helpersPlaywright.tsx b/src/components/Label/__tests__/helpersPlaywright.tsx new file mode 100644 index 0000000000..8f6cb4cd41 --- /dev/null +++ b/src/components/Label/__tests__/helpersPlaywright.tsx @@ -0,0 +1,13 @@ +import {Rocket} from '@gravity-ui/icons'; +import {composeStories} from '@storybook/react'; + +import {Icon} from '../../Icon'; +import type {LabelProps} from '../Label'; +import {Label} from '../Label'; +import * as DefaultLabelStories from '../__stories__/Label.stories'; + +export const LabelStories = composeStories(DefaultLabelStories); + +export const TestLabelWithIcon = (props: Partial) => { + return
here' + } +> + Open a tooltip + +``` + + + +### Со ссылками + + + + + +```tsx + alert('The link is clicked'), + }, + ]} +> + Open a tooltip + +``` + + + +### С кнопкой действия + + + + + +```tsx + console.log('Action button was clicked'), + }} +> + Open a tooltip + +``` + + + +### С автоматическим закрытием, когда курсор находится вне области в течение `delayClosing` + + + + + +```tsx + console.log('Action button was clicked'), + }} +> + Open a tooltip + +``` + + + +## Использование экземпляра + +```tsx +import {Popover, PopoverInstanceProps} from '@gravity-ui/uikit'; + +const popoverRef = useRef(); + +const open = () => { + popoverRef.current?.openTooltip(); +}; + +const close = () => { + popoverRef.current?.closeTooltip(); +}; + +<> + + + +; +``` + +### Свойства экземпляра + +| Имя | Описание | Тип | Значение по умолчанию | +| ------------ | ------------------------------ | :--------: | :-------------------: | +| openTooltip | Открывает тултип `() => void`. | `Function` | | +| closeTooltip | Закрывает тултип `() => void`. | `Function` | | + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------: | :-------------------: | +| anchorRef | Элемент-якорь `Popper.js`, который также может быть `popper.VirtualElement`. | [`PopupAnchorRef`](../Popup/README.md#anchor) | | +| autoclosable | Включает или отключает автоматическое закрытие тултипа при выходе курсора за его пределы. | `boolean` | `true` | +| autoFocus | Если установлено значение `true`, фокус будет перемещен на первый элемент при открытии компонента `Popover`. | `boolean` | | +| behavior | Поведение тултипа при его открытии или закрытии с использованием `openOnHover`: `"immediate"` — без каких-либо задержек, `"delayed"` — с задержкой 300 мс при открытии и закрытии, `"delayedClosing"` — с задержкой 300 мс только при закрытии. Это свойство не будет работать, если заданы `delayOpening` или `delayClosing`. | `"immediate"` `"delayed"` `"delayedClosing"` | `"delayed"` | +| children | Контент, который служит триггером для отображаемого над ним тултипа. Может принимать значения функции `(triggerProps: `[`TriggerProps`](#triggerprops))` => React.ReactNode` или `ReactNode`. | `React.ReactNode` `Function` | | +| className | CSS-класс контрола. | `string` | | +| content | Содержимое тултипа. | `React.ReactNode` | | +| contentClassName | CSS-класс для `content`. | `string` | | +| delayClosing | Пользовательская задержка закрытия, если задано свойство `autoclosable`. | `number` | | +| delayOpening | Пользовательская задержка открытия, если задано свойство `openOnHover`. | `number` | | +| disabled | Отключает возможность изменения состояния открытия. | `boolean` | `false` | +| disablePortal | Отключает рендеринг компонента `Popover` в портале. | `boolean` | `false` | +| focusTrap | Не допускает выхода фокуса за пределы `Popover`, пока он открыт. | `boolean` | | +| forceLinksAppearance | Принудительно применяет стили к ссылкам. | `boolean` | `false` | +| hasArrow | Включает или отключает стрелку тултипа. | `boolean` | `true` | +| hasClose | Включает или отключает кнопку закрытия тултипа. | `boolean` | `false` | +| htmlContent | HTML-содержимое тултипа, которое будет отрисовано с помощью `dangerouslySetInnerHTML`. | `string` | | +| initialOpen | Включает или отключает автоматическое открытие тултипа при загрузке. | `boolean` | `false` | +| links | Ссылки под содержимым. | `[`[`LinkProps`](#linksprops)`]` | | +| offset | Смещение контрола. | `{top: number, left: number}` | | +| onClick | Обратный вызов при клике по элементу-якорю — `(event: React.MouseEvent) => boolean \| Promise`. Если функция возвращает `true`, тултип откроется; в противном случае — нет. | `Function` | | +| onCloseClick | Обработчик клика по кнопке закрытия — `(event: React.MouseEvent) => void`. | `Function` | | +| onOpenChange | Обработчик изменения состояния открытия — `(open: boolean) => void`. Может использоваться для задержки рендеринга содержимого тултипа. | `Function` | | +| openOnHover | Включает или отключает открытие тултипа по ховеру. | `boolean` | `true` | +| placement | Размещение `Popper.js`. | [`PopupPlacement`](../Popup/README.md#placement) | `["right", "bottom"]` | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| restoreFocusRef | Элемент, на который возвращается фокус при закрытии `Popover`. | `React.RefObject` | | +| size | Размер тултипа. | `"s"` `"l"` | `"s"` | +| strategy | [Стратегия](https://popper.js.org/docs/v2/constructors/#strategy) позиционирования `Popper.js`. | `"absolute"` `"fixed"` | `"absolute"` | +| title | Заголовок тултипа. | `string` | | +| theme | Тема тултипа. | `"info"` `"special"` `"announcement"` | `"info"` | +| tooltipActionButton | Свойства кнопки действия. Кнопка не будет отрисована, если не задать это свойство. | [`PopoverButtonProps`](#popoverbuttonprops) | | +| tooltipCancelButton | Свойства кнопки отмены. Кнопка не будет отрисована, если не задать это свойство. | [`PopoverButtonProps`](#popoverbuttonprops) | | +| tooltipClassName | CSS-класс тултипа. | `string` | | +| tooltipContentClassName | CSS-класс содержимого тултипа. | `string` | | +| tooltipOffset | Смещение тултипа относительно контрола. | `[number, number]` | | +| tooltipId | HTML-атрибут `id` для компонента `Popover`. | `string` | | + +### TriggerProps + +| Имя | Описание | Тип | Значение по умолчанию | +| --------- | ------------------------------ | :--------------------------: | :-------------------: | +| onClick | Обработчик события клика. | `React.MouseEventHandler` | | +| onKeyDown | Обработчик события клавиатуры. | `React.KeyboardEventHandler` | | + +### LinkProps + +| Имя | Описание | Тип | Значение по умолчанию | +| ------- | ---------------------------------------------------------------------------------- | :------------------: | :-------------------: | +| text | Текст ссылки. | `string` | | +| href | Атрибут ссылки `href`. | `string` | | +| target | Определяет, где откроется ссылка. | `"_self"` `"_blank"` | | +| onClick | Обработчик события клика — `(event: React.MouseEvent) => void`. | `Function` | | + +### PopoverButtonProps + +| Имя | Описание | Тип | Значение по умолчанию | +| ------- | --------------------------------------------------------------- | :--------: | :-------------------: | +| text | Текст на кнопке. | `string` | | +| onClick | Обработчик события клика — `(event: React.MouseEvent) => void`. | `Function` | | + +| Имя | Описание | +| :---------------------- | :---------------------------- | +| `--g-popover-padding` | Отступы контента. | +| `--g-popover-max-width` | Максимальная ширина контента. | diff --git a/src/components/Popover/README.md b/src/components/Popover/README.md index 774303d967..e61468f7c7 100644 --- a/src/components/Popover/README.md +++ b/src/components/Popover/README.md @@ -8,7 +8,7 @@ import {Popover} from '@gravity-ui/uikit'; ``` -Block with pop-up content +This component allows you to add a section with some pop-up content. ### Simple usage @@ -173,7 +173,7 @@ LANDING_BLOCK--> -### With automatic closing when cursor is outside for `delayClosing` +### With automatic closing when the cursor is outside for `delayClosing` + +# Popover + + + +```tsx +import {Popover} from '@gravity-ui/uikit'; +``` + +Компонент `Popover` позволяет добавить раздел с всплывающим содержимым. + +### Стандартное использование + + + + + +```tsx +Open a tooltip +``` + + + +### С JSX-контентом + + + + + +```tsx +}>Open a tooltip +``` + + + +### С HTML-контентом + + + + + +```tsx +html content. Learn more here' + } +> + Open a tooltip + +``` + + + +### Со ссылками + + + + + +```tsx + alert('The link is clicked'), + }, + ]} +> + Open a tooltip + +``` + + + +### С кнопкой действия + + + + + +```tsx + console.log('Action button was clicked'), + }} +> + Open a tooltip + +``` + + + +### С автоматическим закрытием, когда курсор находится вне области в течение `delayClosing` + + + + + +```tsx + console.log('Action button was clicked'), + }} +> + Open a tooltip + +``` + + + +## Использование экземпляра + +```tsx +import {Popover, PopoverInstanceProps} from '@gravity-ui/uikit'; + +const popoverRef = useRef(); + +const open = () => { + popoverRef.current?.openTooltip(); +}; + +const close = () => { + popoverRef.current?.closeTooltip(); +}; + +<> + + + +; +``` + +### Свойства экземпляра + +| Имя | Описание | Тип | Значение по умолчанию | +| ------------ | ------------------------------ | :--------: | :-------------------: | +| openTooltip | Открывает тултип `() => void`. | `Function` | | +| closeTooltip | Закрывает тултип `() => void`. | `Function` | | + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------: | :-------------------: | +| anchorRef | Элемент-якорь `Popper.js`, который также может быть `popper.VirtualElement`. | [`PopupAnchorRef`](../Popup/README.md#anchor) | | +| autoclosable | Включает или отключает автоматическое закрытие тултипа при выходе курсора за его пределы. | `boolean` | `true` | +| autoFocus | Если установлено значение `true`, фокус будет перемещен на первый элемент при открытии компонента `Popover`. | `boolean` | | +| behavior | Поведение тултипа при его открытии или закрытии с использованием `openOnHover`: `"immediate"` — без каких-либо задержек, `"delayed"` — с задержкой 300 мс при открытии и закрытии, `"delayedClosing"` — с задержкой 300 мс только при закрытии. Это свойство не будет работать, если заданы `delayOpening` или `delayClosing`. | `"immediate"` `"delayed"` `"delayedClosing"` | `"delayed"` | +| children | Контент, который служит триггером для отображаемого над ним тултипа. Может принимать значения функции `(triggerProps: `[`TriggerProps`](#triggerprops))` => React.ReactNode` или `ReactNode`. | `React.ReactNode` `Function` | | +| className | CSS-класс контрола. | `string` | | +| content | Содержимое тултипа. | `React.ReactNode` | | +| contentClassName | CSS-класс для `content`. | `string` | | +| delayClosing | Пользовательская задержка закрытия, если задано свойство `autoclosable`. | `number` | | +| delayOpening | Пользовательская задержка открытия, если задано свойство `openOnHover`. | `number` | | +| disabled | Отключает возможность изменения состояния открытия. | `boolean` | `false` | +| disablePortal | Отключает рендеринг компонента `Popover` в портале. | `boolean` | `false` | +| focusTrap | Не допускает выхода фокуса за пределы `Popover`, пока он открыт. | `boolean` | | +| forceLinksAppearance | Принудительно применяет стили к ссылкам. | `boolean` | `false` | +| hasArrow | Включает или отключает стрелку тултипа. | `boolean` | `true` | +| hasClose | Включает или отключает кнопку закрытия тултипа. | `boolean` | `false` | +| htmlContent | HTML-содержимое тултипа, которое будет отрисовано с помощью `dangerouslySetInnerHTML`. | `string` | | +| initialOpen | Включает или отключает автоматическое открытие тултипа при загрузке. | `boolean` | `false` | +| links | Ссылки под содержимым. | `[`[`LinkProps`](#linksprops)`]` | | +| offset | Смещение контрола. | `{top: number, left: number}` | | +| onClick | Обратный вызов при клике по элементу-якорю — `(event: React.MouseEvent) => boolean \| Promise`. Если функция возвращает `true`, тултип откроется; в противном случае — нет. | `Function` | | +| onCloseClick | Обработчик клика по кнопке закрытия — `(event: React.MouseEvent) => void`. | `Function` | | +| onOpenChange | Обработчик изменения состояния открытия — `(open: boolean) => void`. Может использоваться для задержки рендеринга содержимого тултипа. | `Function` | | +| openOnHover | Включает или отключает открытие тултипа по ховеру. | `boolean` | `true` | +| placement | Размещение `Popper.js`. | [`PopupPlacement`](../Popup/README.md#placement) | `["right", "bottom"]` | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| restoreFocusRef | Элемент, на который возвращается фокус при закрытии `Popover`. | `React.RefObject` | | +| size | Размер тултипа. | `"s"` `"l"` | `"s"` | +| strategy | [Стратегия](https://popper.js.org/docs/v2/constructors/#strategy) позиционирования `Popper.js`. | `"absolute"` `"fixed"` | `"absolute"` | +| title | Заголовок тултипа. | `string` | | +| theme | Тема тултипа. | `"info"` `"special"` `"announcement"` | `"info"` | +| tooltipActionButton | Свойства кнопки действия. Кнопка не будет отрисована, если не задать это свойство. | [`PopoverButtonProps`](#popoverbuttonprops) | | +| tooltipCancelButton | Свойства кнопки отмены. Кнопка не будет отрисована, если не задать это свойство. | [`PopoverButtonProps`](#popoverbuttonprops) | | +| tooltipClassName | CSS-класс тултипа. | `string` | | +| tooltipContentClassName | CSS-класс содержимого тултипа. | `string` | | +| tooltipOffset | Смещение тултипа относительно контрола. | `[number, number]` | | +| tooltipId | HTML-атрибут `id` для компонента `Popover`. | `string` | | + +### TriggerProps + +| Имя | Описание | Тип | Значение по умолчанию | +| --------- | ------------------------------ | :--------------------------: | :-------------------: | +| onClick | Обработчик события клика. | `React.MouseEventHandler` | | +| onKeyDown | Обработчик события клавиатуры. | `React.KeyboardEventHandler` | | + +### LinkProps + +| Имя | Описание | Тип | Значение по умолчанию | +| ------- | ---------------------------------------------------------------------------------- | :------------------: | :-------------------: | +| text | Текст ссылки. | `string` | | +| href | Атрибут ссылки `href`. | `string` | | +| target | Определяет, где откроется ссылка. | `"_self"` `"_blank"` | | +| onClick | Обработчик события клика — `(event: React.MouseEvent) => void`. | `Function` | | + +### PopoverButtonProps + +| Имя | Описание | Тип | Значение по умолчанию | +| ------- | --------------------------------------------------------------- | :--------: | :-------------------: | +| text | Текст на кнопке. | `string` | | +| onClick | Обработчик события клика — `(event: React.MouseEvent) => void`. | `Function` | | + +| Имя | Описание | +| :---------------------- | :---------------------------- | +| `--g-popover-padding` | Отступы контента. | +| `--g-popover-max-width` | Максимальная ширина контента. | diff --git a/src/components/Popup/README.md b/src/components/Popup/README.md index aa0208e512..a0a4c9c76f 100644 --- a/src/components/Popup/README.md +++ b/src/components/Popup/README.md @@ -8,13 +8,12 @@ import {Popup} from '@gravity-ui/uikit'; ``` -`Popup` can be used to display floating content above the page. It is a wrapper around [Popper.js](https://popper.js.org) -with some defaults. To manage `Popup` visibility, use the `open` property. +You can use a `Popup` to display floating content above the page. Technically, it is a wrapper around [Popper.js](https://popper.js.org) with some default values. To manage `Popup` visibility, use the `open` property. The `Popup` child components are rendered inside the [`Portal`](../Portal) component. To disable this behavior, use the `disablePortal` property. ## Anchor -Ref object of the DOM element is passed to the `anchorRef` property to create a `Popper.js` instance. +Ref object of the DOM element is provided to the `anchorRef` property to create a `Popper.js` instance. ## Properties -| Name | Description | Type | Default | -| :------------------- | :--------------------------------------------------------------------------------- | :--------------------------------------: | :------------------------------: | -| altBoundary | `altBoundary` parameter for `Popper.js` `offset` modifier | `boolean` | `false` | -| anchorRef | `Popper.js` anchor element. Can also be `popper.VirtualElement` | `PopupAnchorRef` | | -| autoFocus | While open, the focus will be set to the first interactive element in the content | `boolean` | `false` | -| children | Any React content | `React.ReactNode` | | -| className | HTML `class` attribute for root node | `string` | | -| container | DOM element children to be mounted to | `HTMLElement` | `document.body` | -| contentClassName | HTML `class` attribute for content node | `string` | | -| disableEscapeKeyDown | Do not trigger close on `Esc` | `boolean` | `false` | -| disableLayer | Do not use `LayerManager` on stacking popups | `boolean` | `false` | -| disableOutsideClick | Do not trigger close on outside clicks | `boolean` | `false` | -| disablePortal | Do not use `Portal` for children | `boolean` | `false` | -| focusTrap | Enable focus trapping behavior | `boolean` | `false` | -| hasArrow | Render an arrow pointing to the anchor | `boolean` | `false` | -| id | HTML `id` attribute | `string` | | -| keepMounted | `Popup` will not be removed from the DOM upon hiding | `boolean` | `false` | -| modifiers | `Popper.js` modifiers in addition to default: `arrow`, `offset`, `flip` | `Array` | `[0, 4]` | -| offset | `Popper.js` offset | `[number, number]` | `[0, 4]` | -| onBlur | `blur` event handler | `Function` | | -| onClose | Handle `Popup` close event | `Function` | | -| onEnterKeyDown | `Enter` press event handler | `Function` | | -| onEscapeKeyDown | `Esc` press event handler | `Function` | | -| onFocus | `focus` event handler | `Function` | | -| onMouseEnter | `mouseenter` event handler | `Function` | | -| onMouseLeave | `mouseleave` event handler | `Function` | | -| onOutsideClick | Outside click event handler | `Function` | | -| onTransitionEnter | On start open popup animation | `Function` | | -| onTransitionEntered | On finish open popup animation | `Function` | | -| onTransitionExit | On start close popup animation | `Function` | | -| onTransitionExited | On finish close popup animation | `Function` | | -| open | Manages `Popup` visibility | `boolean` | `false` | -| placement | `Popper.js` placement | `PopupPlacement` `Array` | | -| qa | Test attribute (`data-qa`) | `string` | | -| restoreFocus | If true, the focus will return to the anchor element | `boolean` | `false` | -| restoreFocusRef | Element the focus will be restored to | `React.RefObject` | | -| aria-labelledby | `aria-labelledby` attribute, prefer this attribute if you have visible caption | `string` | | -| aria-label | `aria-label` attribute, use this attribute only if you didn't have visible caption | `string` | | -| aria-modal | The `aria-modal` attribute indicates whether an element is modal when displayed. | `Booleanish` | value of `focusTrap` | -| role | The accessibility role for popup | `string` | `dialog` if `aria-modal` is true | -| strategy | `Popper.js` positioning strategy | `popper.PositioningStrategy` | `[0, 4]` | -| style | HTML `style` attribute for root node | `string` | | +| Name | Description | Type | Default | +| :------------------- | :---------------------------------------------------------------------------------- | :--------------------------------------: | :------------------------------: | +| altBoundary | `altBoundary` parameter for the `Popper.js` `offset` modifier | `boolean` | `false` | +| anchorRef | `Popper.js` anchor element that can also be `popper.VirtualElement` | `PopupAnchorRef` | | +| autoFocus | While open, the focus will be set to the first interactive element in the content | `boolean` | `false` | +| children | Any React content | `React.ReactNode` | | +| className | `class` HTML attribute for the root node | `string` | | +| container | DOM element children to mount | `HTMLElement` | `document.body` | +| contentClassName | `class` HTML attribute for the content node | `string` | | +| disableEscapeKeyDown | Disables triggering close on `Esc` | `boolean` | `false` | +| disableLayer | Disables using `LayerManager` on stacking popups | `boolean` | `false` | +| disableOutsideClick | Disables triggering close on outside clicks | `boolean` | `false` | +| disablePortal | Disables using `Portal` for children | `boolean` | `false` | +| focusTrap | Enables focus trapping behavior | `boolean` | `false` | +| hasArrow | Renders arrow pointing to the anchor | `boolean` | `false` | +| id | `id` HTML attribute | `string` | | +| keepMounted | `Popup` will not be removed from the DOM upon hiding | `boolean` | `false` | +| modifiers | `Popper.js` modifiers in addition to the default one: `arrow`, `offset`, and `flip` | `Array` | `[0, 4]` | +| offset | `Popper.js` offset | `[number, number]` | `[0, 4]` | +| onBlur | `blur` event handler | `Function` | | +| onClose | Handles `Popup` close event | `Function` | | +| onEnterKeyDown | `Enter` press event handler | `Function` | | +| onEscapeKeyDown | `Esc` press event handler | `Function` | | +| onFocus | `focus` event handler | `Function` | | +| onMouseEnter | `mouseenter` event handler | `Function` | | +| onMouseLeave | `mouseleave` event handler | `Function` | | +| onOutsideClick | Outside click event handler | `Function` | | +| onTransitionEnter | Open popup animation on start | `Function` | | +| onTransitionEntered | Open popup animation on finish | `Function` | | +| onTransitionExit | Close popup animation on start | `Function` | | +| onTransitionExited | Close popup animation on finish | `Function` | | +| open | Manages `Popup` visibility | `boolean` | `false` | +| placement | `Popper.js` placement | `PopupPlacement` `Array` | | +| qa | Test attribute (`data-qa`) | `string` | | +| restoreFocus | If true, the focus will return to the anchor element | `boolean` | `false` | +| restoreFocusRef | Element the focus will be restored to | `React.RefObject` | | +| aria-labelledby | `aria-labelledby` attribute. Preferable if you have visible caption | `string` | | +| aria-label | `aria-label` attribute. Use it only if you do not have any visible caption | `string` | | +| aria-modal | Shows whether an element is modal when displayed | `Booleanish` | `focusTrap` value | +| role | Accessibility role for popup | `string` | `dialog` if `aria-modal` is true | +| strategy | `Popper.js` positioning strategy | `popper.PositioningStrategy` | `[0, 4]` | +| style | `style` HTML attribute for root node | `string` | | ## CSS API diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-default-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-default-light-chromium-linux.png new file mode 100644 index 0000000000..e11b1a9379 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-default-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-offset-4-12-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-offset-4-12-light-chromium-linux.png new file mode 100644 index 0000000000..a80482b077 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-offset-4-12-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-default-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-default-light-chromium-linux.png new file mode 100644 index 0000000000..69e781fddf Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-default-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-end-light-chromium-linux.png new file mode 100644 index 0000000000..586a0c5fba Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-light-chromium-linux.png new file mode 100644 index 0000000000..4f3fdac63c Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-start-light-chromium-linux.png new file mode 100644 index 0000000000..33de18d2c4 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-auto-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-end-light-chromium-linux.png new file mode 100644 index 0000000000..82916be3ad Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-light-chromium-linux.png new file mode 100644 index 0000000000..9167d68d53 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-start-light-chromium-linux.png new file mode 100644 index 0000000000..87fd1e6bc6 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-bottom-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-end-light-chromium-linux.png new file mode 100644 index 0000000000..01c4f89b01 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-light-chromium-linux.png new file mode 100644 index 0000000000..3e9c7e7e53 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-start-light-chromium-linux.png new file mode 100644 index 0000000000..99ce31f893 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-left-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-end-light-chromium-linux.png new file mode 100644 index 0000000000..e6f33654c9 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-light-chromium-linux.png new file mode 100644 index 0000000000..da218d5993 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-start-light-chromium-linux.png new file mode 100644 index 0000000000..e08c65531c Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-right-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-end-light-chromium-linux.png new file mode 100644 index 0000000000..7abf7561a7 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-light-chromium-linux.png new file mode 100644 index 0000000000..a205bc6398 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-start-light-chromium-linux.png new file mode 100644 index 0000000000..42a3526b75 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-placement-top-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-default-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-default-light-chromium-linux.png new file mode 100644 index 0000000000..346fe1bfd4 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-default-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-end-light-chromium-linux.png new file mode 100644 index 0000000000..e51e451403 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-light-chromium-linux.png new file mode 100644 index 0000000000..f14261963d Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-start-light-chromium-linux.png new file mode 100644 index 0000000000..6b63836fb1 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-auto-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-end-light-chromium-linux.png new file mode 100644 index 0000000000..03d84bbddf Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-light-chromium-linux.png new file mode 100644 index 0000000000..0e813c6385 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-start-light-chromium-linux.png new file mode 100644 index 0000000000..a0deb87dca Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-bottom-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-end-light-chromium-linux.png new file mode 100644 index 0000000000..f251f9def4 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-light-chromium-linux.png new file mode 100644 index 0000000000..852a9ce1ac Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-start-light-chromium-linux.png new file mode 100644 index 0000000000..1a7da68517 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-left-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-end-light-chromium-linux.png new file mode 100644 index 0000000000..409a9207f2 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-light-chromium-linux.png new file mode 100644 index 0000000000..40dc0105b9 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-start-light-chromium-linux.png new file mode 100644 index 0000000000..097d715992 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-right-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-end-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-end-light-chromium-linux.png new file mode 100644 index 0000000000..47cc466d85 Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-end-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-light-chromium-linux.png new file mode 100644 index 0000000000..7002bacf2c Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-start-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-start-light-chromium-linux.png new file mode 100644 index 0000000000..d18bcab8bd Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-placement-with-arrow-placement-top-start-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-absolute-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-absolute-light-chromium-linux.png new file mode 100644 index 0000000000..feaad816cb Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-absolute-light-chromium-linux.png differ diff --git a/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-fixed-light-chromium-linux.png b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-fixed-light-chromium-linux.png new file mode 100644 index 0000000000..b757eb7ead Binary files /dev/null and b/src/components/Popup/__snapshots__/Popup.visual.test.tsx-snapshots/Popup-smoke-strategy-fixed-light-chromium-linux.png differ diff --git a/src/components/Popup/__stories__/Popup.stories.tsx b/src/components/Popup/__stories__/Popup.stories.tsx index 2824695128..dfbe7252ec 100644 --- a/src/components/Popup/__stories__/Popup.stories.tsx +++ b/src/components/Popup/__stories__/Popup.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Meta, StoryObj} from '@storybook/react'; diff --git a/src/components/Popup/__tests__/Popup.test.tsx b/src/components/Popup/__tests__/Popup.test.tsx index b69f964328..9d0613097a 100644 --- a/src/components/Popup/__tests__/Popup.test.tsx +++ b/src/components/Popup/__tests__/Popup.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Popup/__tests__/Popup.visual.test.tsx b/src/components/Popup/__tests__/Popup.visual.test.tsx new file mode 100644 index 0000000000..2feee45985 --- /dev/null +++ b/src/components/Popup/__tests__/Popup.visual.test.tsx @@ -0,0 +1,87 @@ +import {expect} from '@playwright/experimental-ct-react'; + +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {PopupProps} from '../Popup'; + +import {offsetCases, placementCases, strategyCases} from './cases'; +import {VisualTestQA} from './constants'; +import {TestPopup} from './helpers'; + +test.describe('Popup', {tag: '@Popup'}, () => { + const defaultProps: PopupProps = {}; + + createSmokeScenarios(defaultProps, { + offset: offsetCases, + strategy: strategyCases, + }).forEach(([title, props]) => { + smokeTest(title, async ({mount, page, expectScreenshot}) => { + await mount( +
+

{title}

+ +
, + ); + + await expect(page.getByTestId(VisualTestQA.popupContent)).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + }); + + createSmokeScenarios( + defaultProps, + { + placement: placementCases, + }, + { + scenarioName: 'placement', + }, + ).forEach(([title, props]) => { + smokeTest(title, async ({mount, page, expectScreenshot}) => { + await mount( +
+

{title}

+ +
, + ); + + await expect(page.getByTestId(VisualTestQA.popupContent)).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + }); + + createSmokeScenarios( + { + ...defaultProps, + hasArrow: true, + }, + { + placement: placementCases, + }, + { + scenarioName: 'placement with arrow', + }, + ).forEach(([title, props]) => { + smokeTest(title, async ({mount, page, expectScreenshot}) => { + await mount( +
+

{title}

+ +
, + ); + + await expect(page.getByTestId(VisualTestQA.popupContent)).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + }); +}); diff --git a/src/components/Popup/__tests__/cases.tsx b/src/components/Popup/__tests__/cases.tsx new file mode 100644 index 0000000000..2b780dbf57 --- /dev/null +++ b/src/components/Popup/__tests__/cases.tsx @@ -0,0 +1,22 @@ +import type {Cases, CasesWithName} from '../../../stories/tests-factory/models'; +import type {PopupProps} from '../Popup'; + +export const placementCases: Cases = [ + 'auto', + 'auto-start', + 'auto-end', + 'top', + 'bottom', + 'right', + 'left', + 'top-start', + 'top-end', + 'bottom-start', + 'bottom-end', + 'right-start', + 'right-end', + 'left-start', + 'left-end', +]; +export const offsetCases: CasesWithName = [['4-12', [4, 12]]]; +export const strategyCases: Cases = ['absolute', 'fixed']; diff --git a/src/components/Popup/__tests__/constants.ts b/src/components/Popup/__tests__/constants.ts new file mode 100644 index 0000000000..d930954bd5 --- /dev/null +++ b/src/components/Popup/__tests__/constants.ts @@ -0,0 +1,4 @@ +export const VisualTestQA = { + trigger: 'trigger', + popupContent: 'popup-content', +}; diff --git a/src/components/Popup/__tests__/helpers.tsx b/src/components/Popup/__tests__/helpers.tsx new file mode 100644 index 0000000000..34d2ccdbf4 --- /dev/null +++ b/src/components/Popup/__tests__/helpers.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; + +import type {PopupProps} from '../Popup'; +import {Popup} from '../Popup'; + +import {VisualTestQA} from './constants'; + +export const TestPopup = (props: PopupProps) => { + const anchorRef = React.useRef(null); + + return ( + + { + // nothing + }} + anchorRef={anchorRef} + qa={VisualTestQA.popupContent} + > +
Popup content
+
+
+
+ trigger block +
+
+
+ ); +}; diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index dee2817682..1422cf6a02 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import ReactDOM from 'react-dom'; diff --git a/src/components/Portal/README-ru.md b/src/components/Portal/README-ru.md new file mode 100644 index 0000000000..88786ffcc6 --- /dev/null +++ b/src/components/Portal/README-ru.md @@ -0,0 +1,36 @@ + + +# Portal + + + +```tsx +import {Portal} from '@gravity-ui/uikit'; +``` + +`Portal` — утилитный компонент, который представляет собой простую обертку для [`createPortal`](https://react.dev/reference/react-dom/createPortal) в React и позволяет рендерить дочерние элементы в DOM-узле за пределами родительского компонента. + +## Контейнер + +По умолчанию `Portal` рендерит дочерние элементы в `document.body`. Это можно поменять в свойстве `container`. +Кроме того, можно задать контейнер для всех порталов в поддереве React с помощью компонента `PortalProvder`. + +```tsx +import {Portal, PortalProvider} from '@gravity-ui/uikit' + +const myRoot = document.getElementById('my-root'); + +This is rendered inside document.body +This is rendered inside #my-root node + + This is also rendered inside #my-root + +``` + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------ | :------------------------------------------------------------------------------------------------ | :---------------: | :-------------------: | +| children | Любое содержимое React. | `React.ReactNode` | | +| container | DOM-элемент, в который монтируются дочерние элементы. | `HTMLElement` | `document.body` | +| disablePortal | Если установлено значение `true`, дочерние элементы будут рендериться в стандартной иерархии DOM. | `boolean` | | diff --git a/src/components/Portal/README.md b/src/components/Portal/README.md index adb82adf0f..58085f1248 100644 --- a/src/components/Portal/README.md +++ b/src/components/Portal/README.md @@ -8,13 +8,12 @@ import {Portal} from '@gravity-ui/uikit'; ``` -`Portal` is a utility component, a simple wrapper around React [`createPortal`](https://react.dev/reference/react-dom/createPortal) -that allows you to render children into a DOM node outside the parent component. +`Portal` is a utility component. Basically, it is a simple wrapper around React's [`createPortal`](https://react.dev/reference/react-dom/createPortal) that allows you to render children into a DOM node outside the parent component. ## Container -By default, `Portal` renders its children into `document.body`; however, it can be changed with the `container` property. -Additionally, a container can be provided to all `Portal`s in the React subtree using the `PortalProvder` component. +By default, `Portal` renders its children into `document.body`; however, you can change this with the `container` property. +Additionally, you can provide a container to all `Portal`s in the React subtree using the `PortalProvder` component. ```tsx import {Portal, PortalProvider} from '@gravity-ui/uikit' @@ -30,8 +29,8 @@ const myRoot = document.getElementById('my-root'); ## Properties -| Name | Description | Type | Default | -| :------------ | :---------------------------------------------------- | :---------------: | :-------------: | -| children | Any React content | `React.ReactNode` | | -| container | DOM element children to be mounted | `HTMLElement` | `document.body` | -| disablePortal | If true, renders children within normal DOM hierarchy | `boolean` | | +| Name | Description | Type | Default | +| :------------ | :------------------------------------------------------------- | :---------------: | :-------------: | +| children | Any React content | `React.ReactNode` | | +| container | DOM element's children to mount | `HTMLElement` | `document.body` | +| disablePortal | If true, renders the children within the normal DOM hierarchy. | `boolean` | | diff --git a/src/components/Portal/__stories__/Portal.stories.tsx b/src/components/Portal/__stories__/Portal.stories.tsx index 46a3c44775..ecf1369110 100644 --- a/src/components/Portal/__stories__/Portal.stories.tsx +++ b/src/components/Portal/__stories__/Portal.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryFn} from '@storybook/react'; import {Portal} from '../Portal'; diff --git a/src/components/Portal/__stories__/README.md b/src/components/Portal/__stories__/README.md new file mode 100644 index 0000000000..58085f1248 --- /dev/null +++ b/src/components/Portal/__stories__/README.md @@ -0,0 +1,36 @@ + + +# Portal + + + +```tsx +import {Portal} from '@gravity-ui/uikit'; +``` + +`Portal` is a utility component. Basically, it is a simple wrapper around React's [`createPortal`](https://react.dev/reference/react-dom/createPortal) that allows you to render children into a DOM node outside the parent component. + +## Container + +By default, `Portal` renders its children into `document.body`; however, you can change this with the `container` property. +Additionally, you can provide a container to all `Portal`s in the React subtree using the `PortalProvder` component. + +```tsx +import {Portal, PortalProvider} from '@gravity-ui/uikit' + +const myRoot = document.getElementById('my-root'); + +This is rendered inside document.body +This is rendered inside #my-root node + + This is also rendered inside #my-root + +``` + +## Properties + +| Name | Description | Type | Default | +| :------------ | :------------------------------------------------------------- | :---------------: | :-------------: | +| children | Any React content | `React.ReactNode` | | +| container | DOM element's children to mount | `HTMLElement` | `document.body` | +| disablePortal | If true, renders the children within the normal DOM hierarchy. | `boolean` | | diff --git a/src/components/Progress/Progress.tsx b/src/components/Progress/Progress.tsx index de452af685..d5815cfe67 100644 --- a/src/components/Progress/Progress.tsx +++ b/src/components/Progress/Progress.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {ProgressWithStack} from './ProgressWithStack'; import {ProgressWithValue} from './ProgressWithValue'; diff --git a/src/components/Progress/ProgressInnerText.tsx b/src/components/Progress/ProgressInnerText.tsx index 9a34df4d96..ba5d1285d7 100644 --- a/src/components/Progress/ProgressInnerText.tsx +++ b/src/components/Progress/ProgressInnerText.tsx @@ -1,9 +1,9 @@ -import React from 'react'; +import type * as React from 'react'; import {progressBlock} from './constants'; export interface ProgressInnerTextProps { - text?: string; + text?: React.ReactNode; offset?: number; } diff --git a/src/components/Progress/ProgressStackItem.tsx b/src/components/Progress/ProgressStackItem.tsx index a8f996daa7..5d5d871a76 100644 --- a/src/components/Progress/ProgressStackItem.tsx +++ b/src/components/Progress/ProgressStackItem.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {CnMods} from '../utils/cn'; import {progressBlock} from './constants'; diff --git a/src/components/Progress/ProgressWithStack.tsx b/src/components/Progress/ProgressWithStack.tsx index e0c5f202f8..46cb7d70bb 100644 --- a/src/components/Progress/ProgressWithStack.tsx +++ b/src/components/Progress/ProgressWithStack.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {ProgressInnerText} from './ProgressInnerText'; import {ProgressStackItem} from './ProgressStackItem'; import {progressBlock} from './constants'; diff --git a/src/components/Progress/ProgressWithValue.tsx b/src/components/Progress/ProgressWithValue.tsx index 37e713d223..c3cd360232 100644 --- a/src/components/Progress/ProgressWithValue.tsx +++ b/src/components/Progress/ProgressWithValue.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {ProgressInnerText} from './ProgressInnerText'; import {progressBlock} from './constants'; import type {ProgressWithValueProps} from './types'; diff --git a/src/components/Progress/README-ru.md b/src/components/Progress/README-ru.md new file mode 100644 index 0000000000..4e50dbd5ef --- /dev/null +++ b/src/components/Progress/README-ru.md @@ -0,0 +1,267 @@ + + +# Progress + + + +```tsx +import {Progress} from '@gravity-ui/uikit'; +``` + +Компонент `Progress` отображает текущий ход выполнения операции. Может быть разбит на секции. + +## Тема + +С помощью свойства `theme` можно настроить цвет всего прогресса или его составной части: + + + + + +```tsx + + + + + + +``` + + + +## Состояния + + + + + +```tsx + +``` + + + +## Размер + +Размер `Progress` можно настроить с помощью свойства `size`. Возможные значения: `"xs"`, `"s"` и `"m"`. Свойство `text` поддерживает только размер `"m"`. + + + + + +```tsx + + + +``` + + + +## Брейкпоинты + +Для установки брейкпоинтов в компоненте `Progress` используется свойство `colorStops`. + + + + + +```tsx + + + +``` + + + +## Стек + + + + + +```tsx + + +``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------: | :-------------------: | +| className | HTML-атрибут `class`. | `string` | | +| colorStops | Задает брейкпоинты с темами. | ` Array<{theme: string; stop: number;}>` | | +| colorStopsValue | Устанавливает значение для выбора текущей точки остановки цвета или альтернативное значение для `colorStops`. Диапазон значений — от 0 до 100. | `number` | | +| loading | Включает или отключает состояние `loading`. | `boolean` | `false` | +| size | Задает размер прогресс-бара. Отображение текста доступно только при размере `"m"`. | `string` | `"m"` | +| stack | Конфигурация составного прогресс-бара. Необязательное свойство, если указано `value`. | ` Array` | | +| stackClassName | HTML-атрибут `name` для стека. | `string` | | +| text | Текст внутри прогресс-бара. | `string` | | +| theme | Задает цвет прогресса. | `string` | `"default"` | +| value | Текущее значение прогресса. Диапазон значений — от 0 до 100. Свойство `stack` является необязательным и используется как `maxValue`. | `number` | | + +### `Stack` + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :----------------------------------------------------------------------------------------------------------------------------------- | :---------: | :-------------------: | +| className | HTML-атрибут `class` для элемента стека. | `string` | | +| color | Задает цвет фона в HTML-атрибуте `style`. | `string` | | +| content | Содержимое элемента стека. | `ReactNode` | | +| title | HTML-атрибут `title`. | `string` | | +| theme | Задает цвет элемента стека. | `string` | `"default"` | +| value | Текущее значение прогресса. Диапазон значений — от 0 до 100. Свойство `stack` является необязательным и используется как `maxValue`. | `number` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | + +## API CSS + +| Имя | Описание | +| :------------------------------------- | :-------------------------------------------- | +| `--g-progress-empty-text-color` | Цвет текста для пустого `Progress`. | +| `--g-progress-filled-text-color` | Цвет текста для заполненной части `Progress`. | +| `--g-progress-empty-background-color` | Цвет фона для пустого `Progress`. | +| `--g-progress-filled-background-color` | Цвет текста для заполненной части `Progress`. | diff --git a/src/components/Progress/README.md b/src/components/Progress/README.md index 7eac4d0319..471e756dc5 100644 --- a/src/components/Progress/README.md +++ b/src/components/Progress/README.md @@ -8,11 +8,11 @@ import {Progress} from '@gravity-ui/uikit'; ``` -`Progress` component indicates current operation progress. It can also be divided into sections. +The `Progress` component shows current operation progress. It can also be divided into sections. ## Theme -Use `theme` property to specify color of the whole progress or the composite part. +Use the `theme` property to specify color of the whole progress or the composite part: ## Breakpoints -To set breakpoints of the `Progress` component use the `colorStops` property. +Use the `colorStops` property to set breakpoints of the `Progress` component. | size | Sets the progress bar size. The progress bar text can only be displayed in `"m"` size. | `string` | `"m"` | | stack | Configuration of composite progress bar. Not required if a `value` is provided. | `Array` | | | stackClassName | HTML `class` attribute of stack | `string` | | -| text | Text inside the progress bar | `string` | | +| text | Text inside the progress bar | `ReactNode` | | | theme | Sets progress color | `string` | `"default"` | | value | Current progress value. The available range is from 0 to 100. Using the `stack` property value is optional and is used as maxValue. | `number` | | ### `Stack` -| Name | Description | Type | Default | -| :-------- | :---------------------------------------------------------------------------------------------------------------------------------- | :---------: | :---------: | -| className | HTML `class` attribute of the stack element | `string` | | -| color | Sets background color for the HTML `style` attribute | `string` | | -| content | Stack element content | `ReactNode` | | -| title | HTML `title` attribute | `string` | | -| theme | Sets the stack element color | `string` | `"default"` | -| value | Current progress value. The available range is from 0 to 100. Using the `stack` property value is optional and is used as maxValue. | `number` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :-------- | :------------------------------------------------------------------------------------------------------------------------------------ | :---------: | :---------: | +| className | `class` HTML attribute of the stack element | `string` | | +| color | Sets the background color for the `style` HTML attribute | `string` | | +| content | Stack element content | `ReactNode` | | +| title | `title` HTML attribute | `string` | | +| theme | Sets the stack element color | `string` | `"default"` | +| value | Current progress value. The available range is from 0 to 100. Using the `stack` property value is optional and is used as `maxValue`. | `number` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | ## CSS API diff --git a/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-light-chromium-linux.png b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..0776fc2e76 Binary files /dev/null and b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-light-chromium-linux.png differ diff --git a/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-color-stops-light-chromium-linux.png b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-color-stops-light-chromium-linux.png new file mode 100644 index 0000000000..3f49017503 Binary files /dev/null and b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-color-stops-light-chromium-linux.png differ diff --git a/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-stack-light-chromium-linux.png b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-stack-light-chromium-linux.png new file mode 100644 index 0000000000..407d689c94 Binary files /dev/null and b/src/components/Progress/__snapshots__/Progress.visual.test.tsx-snapshots/Progress-smoke-smoke-with-stack-light-chromium-linux.png differ diff --git a/src/components/Progress/__stories__/Progress.stories.tsx b/src/components/Progress/__stories__/Progress.stories.tsx index 2112494174..9553859897 100644 --- a/src/components/Progress/__stories__/Progress.stories.tsx +++ b/src/components/Progress/__stories__/Progress.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/Progress/__tests__/Progress.visual.test.tsx b/src/components/Progress/__tests__/Progress.visual.test.tsx new file mode 100644 index 0000000000..9638b5c01b --- /dev/null +++ b/src/components/Progress/__tests__/Progress.visual.test.tsx @@ -0,0 +1,115 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import {Progress} from '../Progress'; + +import {loadingCases, sizeCases, themeCases} from './cases'; + +test.describe('Progress', {tag: '@Progress'}, () => { + smokeTest('smoke', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + {}, + { + size: sizeCases, + theme: themeCases, + loading: loadingCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('smoke, with color stops', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + {}, + { + size: sizeCases, + loading: loadingCases, + }, + { + scenarioName: 'with color stops', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('smoke, with stack', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + {}, + { + size: sizeCases, + loading: loadingCases, + }, + { + scenarioName: 'with stack', + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); +}); diff --git a/src/components/Progress/__tests__/cases.tsx b/src/components/Progress/__tests__/cases.tsx new file mode 100644 index 0000000000..7c2fa9f883 --- /dev/null +++ b/src/components/Progress/__tests__/cases.tsx @@ -0,0 +1,13 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {ProgressProps} from '../types'; + +export const sizeCases: Cases = ['xs', 's', 'm']; +export const themeCases: Cases = [ + 'default', + 'success', + 'warning', + 'danger', + 'info', + 'misc', +]; +export const loadingCases: Cases = [true]; diff --git a/src/components/Progress/types.ts b/src/components/Progress/types.ts index faa4cde9d0..676dc71335 100644 --- a/src/components/Progress/types.ts +++ b/src/components/Progress/types.ts @@ -1,4 +1,4 @@ -import type React from 'react'; +import type * as React from 'react'; import type {QAProps} from '../types'; @@ -28,7 +28,7 @@ interface ProgressGeneralProps extends QAProps { export interface ProgressDefaultProps { /** Text inside progress bar */ - text: string; + text: React.ReactNode; /** Theme */ theme: ProgressTheme; /** Size. Text of progress bar is displayed in `m` size only. */ diff --git a/src/components/Radio/README-ru.md b/src/components/Radio/README-ru.md new file mode 100644 index 0000000000..0bc54b2685 --- /dev/null +++ b/src/components/Radio/README-ru.md @@ -0,0 +1,153 @@ + + +# Radio + + + +```tsx +import {Radio} from '@gravity-ui/uikit'; +``` + +Компонент `Radio` позволяет пользователям выбрать один вариант из списка. + +## Состояния + +`Radio` поддерживает следующие состояния: + +- Checked — радио выбрано. +- Disabled — радио недоступно для выбора. + + + + + +```tsx + + + +``` + + + +## Размер + +Размер `Radio` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx + + +``` + + + +## Лейбл + +Лейбл для `Radio` можно задать через свойство `content` или передать его как дочерний элемент. + + + + + +```tsx +
+ +
+ + Content as children + +
+
+``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------- | :------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-------------------: | +| children | Содержимое радио (как правило, лейбл). | `ReactNode` | | +| content | Содержимое радио (альтернатива `children`). | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у радио. | `boolean` | `false` | +| checked | Включает или отключает состояние `checked` у радио. | `boolean` | `false` | +| defaultChecked | Задает начальное состояние `checked` при монтировании компонента. | `boolean` | `false` | +| onUpdate | Срабатывает при изменении состояния радио пользователем и передает значение `checked` как аргумент обратного вызова. | `(checked: boolean) => void` | | +| onChange | Срабатывает при изменении состояния радио пользователем и передает событие изменения как аргумент обратного вызова. | `Function` | | +| onFocus | Обработчик события, вызываемый, когда элемент ввода радио получает фокус. | `Function` | | +| onBlur | Обработчик события, вызываемый, когда элемент ввода радио теряет фокус. | `Function` | | +| size | Определяет размер радио. | `m` `l` | `m` | +| id | HTML-атрибут `id`. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | HTML-атрибут `class`. | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| name | HTML-атрибут `name` для элемента ввода. | `string` | | +| value | Значение контрола. | `string` | | +| indeterminate | Включает или отключает состояние неопределенности радио. | `boolean` | `false` | +| controlProps | Дополнительные свойства базового элемента ввода. | `React.InputHTMLAttributes` | | +| controlRef | Ссылка на базовый элемент ввода. | `React.Ref` | | diff --git a/src/components/Radio/README.md b/src/components/Radio/README.md index de3cd3811f..15a058aba8 100644 --- a/src/components/Radio/README.md +++ b/src/components/Radio/README.md @@ -12,7 +12,7 @@ The `Radio` component allows the users to select a single option from a list of ## States -Radio can have the following states: +`Radio` can have the following states: - Checked: Radio is selected. - Disabled: Radio is unavailable for selection. @@ -72,7 +72,7 @@ LANDING_BLOCK--> ## Label -You can set a label for the `Radio` component using the `content` property or pass it as a child property. +You can assign a label to a `Radio` using the `content` property or provide it as a child property. ## Properties -| Name | Description | Type | Default | -| :------------- | :------------------------------------------------------------------------------------------------ | :-------------------------------------------: | :-----: | -| children | The content of the radio (usually a label). | `ReactNode` | | -| content | The content of the radio (alternative to children). | `ReactNode` | | -| disabled | Toggles the `disabled` state of the radio. | `boolean` | `false` | -| checked | Toggles the checked state of the radio. | `boolean` | `false` | -| defaultChecked | Sets the initial checked state when the component is mounted. | `boolean` | `false` | -| onUpdate | Fires when the radio state is changed by the user. Provides checked value as a callback argument. | `(checked: boolean) => void` | | -| onChange | Fires when the radio state is changed by the user. Provides change event as a callback argument. | `Function` | | -| onFocus | Event handler used when the radio input element receives focus. | `Function` | | -| onBlur | Event handler used when the radio input element loses focus. | `Function` | | -| size | Sets the size of the radio. | `m` `l` | `m` | -| id | HTML `id` attribute | `string` | | -| qa | HTML `data-qa` attribute, used for testing. | `string` | | -| style | HTML `style` attribute | `React.CSSProperties` | | -| className | HTML `class` attribute | `string` | | -| title | HTML `title` attribute | `string` | | -| name | HTML `name` attribute for the input element. | `string` | | -| value | Control value | `string` | | -| indeterminate | Toggles the indeterminate state of the radio. | `boolean` | `false` | -| controlProps | Additional props for the underlying input element. | `React.InputHTMLAttributes` | | -| controlRef | Ref to the underlying input element. | `React.Ref` | | +| Name | Description | Type | Default | +| :------------- | :------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----: | +| children | The content of the radio (usually, a label). | `ReactNode` | | +| content | The content of the radio (alternative to children). | `ReactNode` | | +| disabled | Toggles the `disabled` state of the radio. | `boolean` | `false` | +| checked | Toggles the `checked` state of the radio. | `boolean` | `false` | +| defaultChecked | Sets the initial checked state when the component is mounted | `boolean` | `false` | +| onUpdate | Fires when the radio state is changed by the user and provides the checked value as a callback argument. | `(checked: boolean) => void` | | +| onChange | Fires when the radio state is changed by the user and provides the change event as a callback argument. | `Function` | | +| onFocus | Event handler to use when the radio input element receives focus. | `Function` | | +| onBlur | Event handler to use when the radio input element loses focus. | `Function` | | +| size | Sets the size of the radio. | `m` `l` | `m` | +| id | `id` HTML attribute | `string` | | +| qa | `data-qa` HTML attribute, used for testing. | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| className | `class` HTML attribute | `string` | | +| title | `title` HTML attribute | `string` | | +| name | `name` HTML attribute for the input element | `string` | | +| value | Control value | `string` | | +| indeterminate | Toggles the indeterminate state of the radio. | `boolean` | `false` | +| controlProps | Additional propeties for the underlying input element | `React.InputHTMLAttributes` | | +| controlRef | Ref to the underlying input element | `React.Ref` | | diff --git a/src/components/Radio/Radio.tsx b/src/components/Radio/Radio.tsx index 5604a586d1..25e3acd0e8 100644 --- a/src/components/Radio/Radio.tsx +++ b/src/components/Radio/Radio.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useRadio} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-dark-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-dark-chromium-linux.png new file mode 100644 index 0000000000..73f62a9cfa Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-dark-chromium-linux.png differ diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-dark-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-dark-chromium-linux.png new file mode 100644 index 0000000000..99cd12ce7d Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-dark-chromium-linux.png differ diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-light-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-light-chromium-linux.png new file mode 100644 index 0000000000..fbc2ef046e Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-default-checked-light-chromium-linux.png differ diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-dark-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-dark-chromium-linux.png new file mode 100644 index 0000000000..9a53b8ee7c Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-dark-chromium-linux.png differ diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-light-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-light-chromium-linux.png new file mode 100644 index 0000000000..ede63e0e3f Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-disabled-light-chromium-linux.png differ diff --git a/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-light-chromium-linux.png b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..a0943a31f8 Binary files /dev/null and b/src/components/Radio/__snapshots__/Radio.visual.test.tsx-snapshots/Radio-smoke-light-chromium-linux.png differ diff --git a/src/components/Radio/__stories__/Radio.stories.tsx b/src/components/Radio/__stories__/Radio.stories.tsx index 0615d0e018..3cc80444d3 100644 --- a/src/components/Radio/__stories__/Radio.stories.tsx +++ b/src/components/Radio/__stories__/Radio.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; import {Showcase} from '../../../demo/Showcase'; diff --git a/src/components/Radio/__stories__/RadioShowcase.tsx b/src/components/Radio/__stories__/RadioShowcase.tsx index c5ee53d4c1..57f3822224 100644 --- a/src/components/Radio/__stories__/RadioShowcase.tsx +++ b/src/components/Radio/__stories__/RadioShowcase.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Showcase} from '../../../demo/Showcase'; import {ShowcaseItem} from '../../../demo/ShowcaseItem'; import {Radio} from '../Radio'; diff --git a/src/components/Radio/__tests__/Radio.test.tsx b/src/components/Radio/__tests__/Radio.test.tsx index 3596a84694..3ab4bde0aa 100644 --- a/src/components/Radio/__tests__/Radio.test.tsx +++ b/src/components/Radio/__tests__/Radio.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Radio/__tests__/Radio.visual.test.tsx b/src/components/Radio/__tests__/Radio.visual.test.tsx new file mode 100644 index 0000000000..204ac56e20 --- /dev/null +++ b/src/components/Radio/__tests__/Radio.visual.test.tsx @@ -0,0 +1,93 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {RadioProps} from '../Radio'; +import {Radio} from '../Radio'; + +import {sizeCases} from './cases'; + +test.describe('Radio', {tag: '@Radio'}, () => { + const defaultProps: RadioProps = { + children: 'Test', + value: '1', + }; + + const commonPropsCases = { + size: sizeCases, + } as const; + + smokeTest('', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios(defaultProps, { + ...commonPropsCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('disabled', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + disabled: true, + }, + { + ...commonPropsCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('default checked', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + defaultChecked: true, + }, + { + ...commonPropsCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); +}); diff --git a/src/components/Radio/__tests__/cases.tsx b/src/components/Radio/__tests__/cases.tsx new file mode 100644 index 0000000000..fcf67f2ec9 --- /dev/null +++ b/src/components/Radio/__tests__/cases.tsx @@ -0,0 +1,4 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {RadioProps} from '../Radio'; + +export const sizeCases: Cases = ['m', 'l']; diff --git a/src/components/RadioButton/README-ru.md b/src/components/RadioButton/README-ru.md new file mode 100644 index 0000000000..b72ebec334 --- /dev/null +++ b/src/components/RadioButton/README-ru.md @@ -0,0 +1,279 @@ + + +# RadioButton + + + +```tsx +import {RadioButton} from '@gravity-ui/uikit'; +``` + +Компонент `RadioButton` используется для создания группы кнопок с зависимой фиксацией (т.н. «радиокнопок»), где пользователи могут выбрать только один вариант из нескольких предложенных. + +### Отключенное состояние + + + + + +```tsx +const options: RadioButtonOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, +]; +; +``` + + + +### Размер + +Размер `RadioButton` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx + const options: RadioButtonOption[] = [ + {value: 'Value 1', content: 's'}, + {value: 'Value 2', content: 'm'}, + {value: 'Value 3', content: 'l'}, + {value: 'Value 4', content: 'xl'}, + ]; + + + + +``` + + + +### Ширина + +Ширину `RadioButton` можно настроить с помощью свойства `width`. + + + + + +```tsx +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+``` + + + +### Свойства + +| Имя | Описание | Тип | По умолчанию | +| :----------- | :------------------------------------------------------------------------------------------------------------------------ | :-----------------------: | :----------: | +| children | Содержимое радиокнопки. | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у радиокнопки. | `boolean` | `false` | +| options | Опции радиокнопки. | `RadioButtonOption[]` | | +| defaultValue | Задает начальное значение состояния компонента при его монтировании. | `string` | | +| onUpdate | Срабатывает при изменении состояния радиокнопки пользователем и передает новое значение как аргумент обратного вызова. | `(value: string) => void` | | +| onChange | Срабатывает при изменении состояния радиокнопки пользователем и передает событие изменения как аргумент обратного вызова. | `Function` | | +| onFocus | Обработчик события, вызываемый, когда элемент ввода радио получает фокус. | `Function` | | +| onBlur | Обработчик события, вызываемый, когда элемент ввода радио теряет фокус. | `Function` | | +| width | Определяет ширину радиокнопки. | `auto - max` | | +| size | Определяет размер радиокнопки. | `s` `m` `l` `xl` | `m` | +| name | HTML-атрибут `name` для элемента ввода. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | HTML-атрибут `class`. | `string` | | + +## RadioButton.Option + +`RadioButton` также имеет вложенный компонент `Option`. Его можно использовать для создания вариантов радиокнопок внутри `RadioButton`. + + + + + +```tsx +const options: RadioButtonOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, +]; + + + + +; +``` + + + +### Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------- | :--------------------------------------------------- | :---------: | :-------------------: | +| children | Содержимое радио (как правило, лейбл). | `ReactNode` | | +| content | Содержимое радио (альтернатива `children`). | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у радио. | `boolean` | `false` | +| value | Значение контрола. | `string` | | diff --git a/src/components/RadioButton/README.md b/src/components/RadioButton/README.md index 547a8a61e0..ddf17f9c4e 100644 --- a/src/components/RadioButton/README.md +++ b/src/components/RadioButton/README.md @@ -50,7 +50,7 @@ const options: RadioButtonOption[] = [ ### Size -To control the size of the `RadioButton`, use the `size` property. The default size is `m`. +Use the `size` property to manage the `RadioButton` size. The default size is `m`. ### Width -To control the width of the `RadioButton`, use the `width` property. +Use the `width` property to manage the `RadioButton` width: ### Properties -| Name | Description | Type | Default | -| :----------- | :--------------------------------------------------------------------------------------------------- | :-----------------------: | :-----: | -| children | The content of the radio button. | `ReactNode` | | -| disabled | Toggles the `disabled` state of the radio button. | `boolean` | `false` | -| options | Options for radio button. | `RadioButtonOption[]` | | -| defaultValue | Sets the initial value state when the component is mounted. | `string` | | -| onUpdate | Fires when the user changes the radio button state. Provides the new value as a callback's argument. | `(value: string) => void` | | -| onChange | Fires when the user changes the radio button state. Provides change event as a callback's argument. | `Function` | | -| onFocus | Event handler for when the radio input element receives focus. | `Function` | | -| onBlur | Event handler for when the radio input element loses focus. | `Function` | | -| width | Sets the width of the radio button. | `auto - max` | | -| size | Sets the size of the radio button. | `s` `m` `l` `xl` | `m` | -| name | HTML `name` attribute for the input element. | `string` | | -| qa | HTML `data-qa` attribute, used in tests. | `string` | | -| style | HTML `style` attribute | `React.CSSProperties` | | -| className | HTML `class` attribute | `string` | | +| Name | Description | Type | Default | +| :----------- | :------------------------------------------------------------------------------------------------------- | :-----------------------: | :-----: | +| children | Content of the radio button. | `ReactNode` | | +| disabled | Toggles the `disabled` state of the radio button. | `boolean` | `false` | +| options | Options for radio button. | `RadioButtonOption[]` | | +| defaultValue | Sets the initial value state when the component is mounted. | `string` | | +| onUpdate | Fires when the user changes the radio button state and provides the new value as a callback argument. | `(value: string) => void` | | +| onChange | Fires when the user changes the radio button state and provides the change event as a callback argument. | `Function` | | +| onFocus | Event handler to use when the radio input element receives focus. | `Function` | | +| onBlur | Event handler to use when the radio input element loses focus. | `Function` | | +| width | Sets the width of the radio button. | `auto - max` | | +| size | Sets the size of the radio button. | `s` `m` `l` `xl` | `m` | +| name | `name` HTML attribute for the input element. | `string` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| className | `class` HTML attribute | `string` | | ## RadioButton.Option -The `RadioButton` component also exports a nested `Option` component. You can use it to create radio button options within the `RadioButton`. +The `RadioButton` component also exports a nested `Option` component. You can use it to create radio button options within a `RadioButton`. + +# RadioGroup + + + +```tsx +import {RadioGroup} from '@gravity-ui/uikit'; +``` + +Компонент `RadioGroup` (радиогруппа) используется для создания группы, где пользователи могут выбрать только один вариант из нескольких предложенных. + +### Отключенное состояние + + + + + +```tsx +const options: RadioGroupOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, +]; +; +``` + + + +### Размер + +Размер `RadioGroup` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx + const options: RadioGroupOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, + ]; + + +``` + + + +### Направление + +Направление расположения `RadioGroup` можно настроить с помощью свойства `direction`. Направление по умолчанию — `horizontal`. + + + + + +```tsx + const options: RadioGroupOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, + ]; + + +``` + + + +### Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------------- | :------------------------------------------------------------------------------------------------------------------ | :-----------------------: | :-------------------: | +| children | Содержимое радиогруппы. | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у радиогруппы. | `boolean` | `false` | +| options | Варианты для радиогруппы. | `RadioGroupOption[]` | | +| optionClassName | HTML-атрибут `class` для дочерних элементов радиогруппы. | `string` | | +| direction | Определяет направление расположения радиогруппы. | `horizontal - vertical` | `"horizontal"` | +| defaultValue | Задает начальное значение состояния компонента при его монтировании. | `string` | | +| onUpdate | Срабатывает при изменении состояния радио пользователем и передает новое значение как аргумент обратного вызова. | `(value: string) => void` | | +| onChange | Срабатывает при изменении состояния радио пользователем и передает событие изменения как аргумент обратного вызова. | `Function` | | +| size | Определяет размер радиогруппы. | `m` `l` | `m` | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | HTML-атрибут `class`. | `string` | | + +## RadioGroup.Option + +`RadioGroup` также имеет вложенный компонент `Option`, который является эквивалентом `Radio` и может быть использован для создания вариантов радио в рамках `RadioGroup`. + + + + + +```tsx +const options: RadioGroupOption[] = [ + {value: 'Value 1', content: 'Value 1'}, + {value: 'Value 2', content: 'Value 2'}, + {value: 'Value 3', content: 'Value 3'}, +]; + + + + +; +``` + + + +### Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------- | :--------------------------------------------------- | :---------: | :-------------------: | +| children | Содержимое радио (как правило, лейбл). | `ReactNode` | | +| content | Содержимое радио (альтернатива `children`). | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у радио. | `boolean` | `false` | +| value | Значение контрола. | `string` | | diff --git a/src/components/RadioGroup/README.md b/src/components/RadioGroup/README.md index be2cf09d20..25c9234c3c 100644 --- a/src/components/RadioGroup/README.md +++ b/src/components/RadioGroup/README.md @@ -50,7 +50,7 @@ const options: RadioGroupOption[] = [ ### Size -To control the size of the `RadioGroup`, use the `size` property. The default size is `m`. +Use the `size` property to manage the `RadioGroup` size. The default size is `m`. ### Direction -To control the direction of the `RadioGroup`, use the `direction` property. The default direction is `horizontal`. +Use the `direction` property to manage the `RadioGroup` direction. The default direction is `horizontal`. ### Properties -| Name | Description | Type | Default | -| :-------------- | :----------------------------------------------------------------------------------------------- | :-----------------------: | :------------: | -| children | The content of the radio group. | `ReactNode` | | -| disabled | Toggles the `disabled` state of the radio group. | `boolean` | `false` | -| options | Options for radio group. | `RadioGroupOption[]` | | -| optionClassName | HTML `class` attribute for the radio children. | `string` | | -| direction | Sets the direction of the radio group. | `horizontal - vertical` | `"horizontal"` | -| defaultValue | Sets the initial value state when the component is mounted. | `string` | | -| onUpdate | Fires when the user changes the radio state. Provides the new value as a callback's argument. | `(value: string) => void` | | -| onChange | Fires when the user changes the radio state. Provides the change event as a callback's argument. | `Function` | | -| size | Sets the size of the radio group. | `m` `l` | `m` | -| qa | HTML `data-qa` attribute, used in tests. | `string` | | -| style | HTML `style` attribute | `React.CSSProperties` | | -| className | HTML `class` attribute | `string` | | +| Name | Description | Type | Default | +| :-------------- | :------------------------------------------------------------------------------------------------ | :-----------------------: | :------------: | +| children | The content of the radio group. | `ReactNode` | | +| disabled | Toggles the `disabled` state of the radio group. | `boolean` | `false` | +| options | Options for radio group. | `RadioGroupOption[]` | | +| optionClassName | `class` HTML attribute for the radio children. | `string` | | +| direction | Determines the direction of the radio group. | `horizontal - vertical` | `"horizontal"` | +| defaultValue | Sets the initial value state when the component is mounted. | `string` | | +| onUpdate | Fires when the user changes the radio state and provides the new value as a callback argument. | `(value: string) => void` | | +| onChange | Fires when the user changes the radio state and provides the change event as a callback argument. | `Function` | | +| size | Determines the size of the radio group. | `m` `l` | `m` | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| className | `class` HTML attribute | `string` | | ## RadioGroup.Option -The `RadioGroup` component also exports a nested `Option` component equivalent to the `Radio` component, which can be used to create radio options within the `RadioGroup`. +The `RadioGroup` component also exports a nested `Option` component equivalent to `Radio`, which can be used to create radio options within the `RadioGroup`. + +# Select + + + +```tsx +import {Select} from '@gravity-ui/uikit'; +``` + +Компонент `Select` — это контрол, который предоставляет список вариантов для выбора. + +## `Options` (варианты) + +Варианты для выбора. + +### Определение вариантов + +Варианты можно определять в виде массива объектов или в качестве дочерних элементов компонента. Первый способ подходит для случаев, когда варианты требуют сложной подготовки и, возможно, запоминания. Второй способ удобен, когда вариантов немного и их настройка не требует сложных вычислений. + +#### Одноуровневый список + + + + + +```tsx +// Array of objects + + Value 1 + Value 2 + Value 3 + Value 4 + +``` + + + +#### Группированный список + + + + + +```tsx +// Array of objects + + + + + + + + + + +``` + + + +### Хранение данных в вариантах + +С помощью свойства `option.data` можно определить и сохранить уникальные данные в каждом варианте. Это может быть полезно при необходимости обогащения данных с использованием обратного вызова `onUpdate` или, например, при отрисовке вариантов с помощью `renderOption`. + +## Выбор нескольких вариантов + +Чтобы включить множественный выбор, используйте свойство `multiple`. Значение по умолчанию — `false`. + + + + + +```tsx + +``` + + + +### Счетчик + +С помощью свойства `hasCounter` в компонент можно добавить счетчик выбранных вариантов. + + + + + +```tsx + +``` + + + +## Варианты фильтрации + +Для активации секции фильтрации используйте свойство `filterable`. Значение по умолчанию — `false`. + + + + + +```tsx + +``` + + + +## Размер + +Чтобы задать дефолтный размер контролов и вариантов, используйте свойство `size`. Размер по умолчанию — `m`. + + + + + +```tsx + + + + +``` + + + +## Ширина контрола + +По умолчанию ширина контрола растягивается, чтобы соответствовать ширине содержимого выбранных вариантов. Вы можете самостоятельно регулировать ширину с помощью свойства `width`: + +`'max'` — растягивает ширину контрола на всю ширину родительского элемента. + +`number` — применяет ширину в пикселях. + + + +## Ширина всплывающего окна. + +Ширину всплывающего окна можно изменять с помощью свойства `popupWidth`. Возможные значения: + +`'fit'` — применяет ширину контрола. + +`number` — применяет ширину в пикселях. + +Особенности поведения по умолчанию: + +- Ширина всплывающего окна соответствует ширине самого широкого варианта, но не превышает `90vw`. Это не применимо, если используется [виртуализация](#virtualized-list). + +- Узкие варианты растягиваются до ширины контрола. + + + +### Виртуализированный список + +Для оптимального отображения большого количества вариантов в компоненте `Select`предусмотрен встроенный инструмент виртуализации списка. Виртуализация включается, когда количество вариантов превышает пороговое значение (по умолчанию `50`). Пороговое значение можно изменить с помощью свойства `virtualizationThreshold`. + +При включении виртуализации к элементу всплывающего окна применяются определенные ограничения: + +- Ширина всплывающего окна больше не изменяется в зависимости от длины самого длинного варианта. + +- Минимальная ширина всплывающего окна равна ширине контрола или `100px`, если ширина контрола меньше `100px`. + + + +## Расширенное использование + +Существует множество способов настроить `Select` более тонко. + +### Рендеринг пользовательского контрола + +Для создания пользовательского контрола используйте свойство `renderControl`. +Обратите внимание, что для правильной работы контрола необходимо передать все аргументы в узел (как при использовании стандартной конфигурации). + + + + + +```tsx +import {Button} from '@gravity-ui/uikit'; + +const MyComponent = () => { + const renderControl: SelectProps['renderControl'] = ({onClick, onKeyDown, ref}) => { + return ( + + ); + }; + + return ; +}; +``` + + + +### Отображение секции пользовательской фильтрации + +Для отображения секции пользовательской фильтрации используйте свойство `renderFilter` и установите `filterable` в значение `true`. +Обратите внимание, что для правильной работы фильтра необходимо передать все аргументы в узел (как при использовании стандартной конфигурации). + + + + + +```tsx +import {Button, TextInput} from '@gravity-ui/uikit'; +import type {SelectProps} from '@gravity-ui/uikit'; + +const MyComponent = () => { + const renderFilter: SelectProps['renderFilter'] = (props) => { + const {value, ref, onChange, onKeyDown} = props; + + return ( +
+ + +
+ ); + }; + + return ( + + ); +}; +``` + + + +### Отображение пользовательских вариантов + +Для отображения пользовательских вариантов используйте свойство `renderOption`: + + + + + +```tsx +import type {SelectProps} from '@gravity-ui/uikit'; + +const MyComponent = () => { + const renderOption: SelectProps['renderOption'] = (option) => { + return
{option.children}
; + }; + + return ( + + ); +}; +``` + + + +### Отображение выбранных пользовательских вариантов + +Для отображения выбранных пользовательских вариантов используйте свойство `renderOption`: + + + + + +```tsx +import type {SelectProps} from '@gravity-ui/uikit'; + +const MyComponent = () => { + const renderSelectedOption: SelectProps['renderSelectedOption'] = (option) => { + return
{option.children}
; + }; + + return ( + + ); +}; +``` + + + +### Отображение вариантов с разной высотой + +Варианты имеют фиксированную высоту, заданную в свойстве `size`. Если нужно отобразить варианты с разной высотой, используйте свойство `option.data`, которое будет содержать информацию о требуемой высоте варианта, а также `getOptionHeight` для установки этого значения. + + + + + +```tsx +import type {SelectProps} from '@gravity-ui/uikit'; + +const MyComponent = () => { + const getOptionHeight: SelectProps['getOptionHeight'] = (option) => option.data.height; + + return ( + + ); +}; +``` + + + +### Отображение пользовательских всплывающих окон + +Для отображения пользовательских всплывающих окон используйте свойство `renderPopup`. + + + + + +```tsx +import type {SelectProps} from '@gravity-ui/uikit'; + +const renderPopup: SelectProps['renderPopup'] = ({renderList, renderFilter}) => { + return ( + + {renderFilter()} +
+ {renderList()} + + ); +}; + +const MyComponent = () => { + return ( + + ); +}; +``` + + + +### `Error` (ошибка) + +Это состояние `Select` указывает на некорректный ввод данных пользователем. Для изменения внешнего представления `Select` примените свойство `validationState`, задав ему значение `"invalid"`. Опционально можно задать текст сообщения об ошибке через свойство `errorMessage`. По умолчанию текст сообщения выводится вне компонента. +Место вывода сообщения можно изменить с помощью свойства `errorPlacement`. + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- | +| className | Имя класса контрола. | `string` | | +| defaultValue | Значения по умолчанию для выбранных вариантов в случае использования неуправляемого состояния. | `string[]` | | +| disabled | Указывает на то, что пользователь не может взаимодействовать с контролом. | `boolean` | `false` | +| [filterable](#filtering-options) | Указывает на то, что всплывающее окно выбора содержит секцию фильтрации. | `boolean` | `false` | +| filterOption | Используется для сравнения варианта со значением фильтра. | `function` | | +| filterPlaceholder | Текст-заглушка по умолчанию для поля ввода фильтра. | `string` | | +| [getOptionHeight](#render-options-with-different-heights) | Используется для задания высоты пользовательских вариантов. | `function` | | +| getOptionGroupHeight | Используется для задания высоты группы пользовательских вариантов. | `function` | | +| hasClear | Позволяет отображать иконку для очистки выбранных вариантов. | `boolean` | `false` | +| id | HTML-атрибут `id`. | `string` | | +| label | Лейбл контрола. | `string` | | +| loading | Добавляет элемент загрузки в конец списка вариантов. Работает как постоянный индикатор загрузки, пока список вариантов пуст. | `boolean` | | +| [multiple](#selecting-multiple-options) | Указывает на наличие возможности выбора несколько вариантов в списке. | `boolean` | `false` | +| name | Имя контрола. | `string` | | +| onBlur | Обработчик, который вызывается, когда элемент теряет фокус. | `function` | | +| filter | Контролируемое значение фильтра. | `string` | `''` | +| onFilterChange | Срабатывает при каждом изменении фильтра. | `function` | | +| onFocus | Обработчик, который вызывается, когда элемент получает фокус. | `function` | | +| onLoadMore | Срабатывает, когда индикатор загрузки становится видимым. | `function` | | +| onOpenChange | Срабатывает при каждом изменении видимости всплывающего окна. | `function` | | +| onUpdate | Срабатывает, когда пользователь подтверждает изменение значения `Select`. | `function` | | +| [options](#options) | Варианты для выбора. | `(SelectOption \| SelectOptionGroup)[]` | | +| pin | Вид границ контрола. | `string` | `'round-round'` | +| placeholder | Текст-заглушка. | `string` | | +| popupClassName | Имя класса (`className`) для всплывающего окна со списком вариантов. | `string` | | +| popupPlacement | Размещение `Popper.js`. | `PopupPlacement` `Array` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` | +| [popupWidth](#popup-width) | Ширина всплывающего окна. | `number \| 'fit' \| 'outfit'` | `'outfit'` | +| qa | Атрибут идентификатора для тестирования (`data-qa`). | `string` | | +| [renderControl](#render-custom-control) | Используется для рендеринга пользовательского контрола. | `function` | | +| renderEmptyOptions | Используется для рендеринга узла для пустого списка вариантов. | `function` | | +| [renderFilter](#render-custom-filter-section) | Используется для рендеринга секции пользовательской фильтрации. | `function` | | +| [renderOption](#render-custom-options) | Используется для рендеринга пользовательских вариантов. | `function` | | +| renderOptionGroup | Используется для рендеринга групп пользовательских вариантов. | `function` | | +| [renderSelectedOption](#render-custom-selected-options) | Используется для рендеринга выбранных пользователем вариантов. | `function` | | +| [renderPopup](#render-custom-popup) | Используется для рендеринга содержимого пользовательского всплывающего окна. | `function` | | +| [size](#size) | Размер контрола / вариантов. | `string` | `'m'` | +| value | Значения для выбранных вариантов. | `string[]` | | +| view | Вид контрола. | `string` | `'normal'` | +| [virtualizationThreshold](#virtualized-list) | Порог количества вариантов, после которого включается виртуализация. | `number` | `50` | +| [width](#control-width) | Ширина контрола | `string \| number` | `undefined` | +| errorMessage | Текст ошибки. | `string` | | +| errorPlacement | Положение отображения ошибки. | `outside` `inside` | `outside` | +| validationState | Состояние валидации. | `"invalid"` | | +| [hasCounter](#counter) | Показывает количество выбранных вариантов. Счетчик появляется только тогда, когда включен [множественный](#selecting-multiple-options) выбор. | `boolean` | + +## API CSS + +| Имя | Описание | +| :------------------------------- | :-------------------------------------------------------------- | +| `--g-select-focus-outline-color` | Цвет обводки при фокусе на элементе (по умолчанию отсутствует). | diff --git a/src/components/Select/README.md b/src/components/Select/README.md index cd5031273b..e576e84413 100644 --- a/src/components/Select/README.md +++ b/src/components/Select/README.md @@ -8,7 +8,7 @@ import {Select} from '@gravity-ui/uikit'; ``` -`Select` represents a control that provides a list of options that user can select. +`Select` is a control that provides a list of options that a user can select. ## Options @@ -16,7 +16,7 @@ Options to select. ### Defining options -You can define options as an array of objects or as the children of a component. The first approach is convenient for cases where options require complex preparation and possible memoization. The second approach is convenient when there are few options, and their configuration does not require complex calculations. +You can define options as an array of objects or as the children of a component. The first approach is useful for cases where options require complex preparation and, possibly, memorization. The second one is convenient when there are few options, and their configuration does not require complex calculations. #### Flat list @@ -200,11 +200,11 @@ LANDING_BLOCK--> ### Storing data in options -You can define and store uniq data in each option by using `option.data` property. This can be useful when you need to enrich the data when using the `onUpdate` callback or, for example, when drawing your options with `renderOption`. +You can define and store unique data in each option by using the `option.data` property. This can be useful when you need to enrich the data when using the `onUpdate` callback or, for example, when drawing your options with `renderOption`. ## Selecting multiple options -To enable multiple selection use the `multiple` property. Default to `false`. +To enable multiple selection, use the `multiple` property. Its default value is `false`. ### Counter -You can add counter of the selected items to the component by using property `hasCounter`. +You can add a counter of the selected items to the component using the `hasCounter` property. ## Filtering options -To enable filter section use the `filterable` property. Default to `false`. +To enable filter section, use the `filterable` property. Its default value is `false`. ## Size -To manage default contols and options sizes use the `size` property. Default value is `m`. +To manage the default control and option size, use the `size` property. Its default size is `m`. ## Control width -By default, the width of the control stretches to match the width of the content of the selected options. You can manage it by using `width` property: +By default, the control width stretches to match the width of the content of the selected options. You can manage it by using the `width` property: -`'max'` - stretches to the full width of the parent. +`'max'`: Stretches to the full width of the parent. -`number` - apply width in pixels. +`number`: Applies width in pixels. ## Popup width -Popup width managed by the `popupWidth` property. Available values: +You can manage the popup width with the `popupWidth` property. The available values are: -`'fit'` - apply control width. +`'fit'`: Apply control width. -`number` - apply width in pixels. +`number`: Apply width in pixels. -There are some points about default behaviour: +Points to note about the default behavior: -- The width of the popup is equal to the width of the widest option, but not wider than `90vw`. Does not work in case of using [virtualization](#virtualized-list). +- The popup width is equal to the width of the widest option, but not wider than `90vw`. This does not apply in case you use [virtualization](#virtualized-list). - Narrow options are stretched to fit the width of the control. @@ -562,11 +562,11 @@ LANDING_BLOCK--> ### Virtualized list -For optimal display of a large number of options, `Select` has a built-in list virtualization mechanism. Virtualization is enabled after overcoming the threshold of the number of options (`50` by default). You can control this value using the `virtualizationThreshold` property. +For optimal display of a large number of options, `Select` has a built-in list virtualization tool. Virtualization is enabled after overcoming the threshold of the number of options (`50` by default). You can manage this value using the `virtualizationThreshold` property. -When using virtualization, some restrictions are imposed on the popup element: +When using virtualization, some restrictions apply to the popup element: -- The popup width stops adjusting to the length of the longest option. +- The popup width no longer gets adjusted to the length of the longest option. - The minimum width of the popup is equal to the width of the control, or `100px` if the control is shorter. @@ -652,12 +652,12 @@ LANDING_BLOCK--> ## Advanced usage -There are many ways to add uniqueness to your `Select`. +There are many ways to customize your `Select`. -### Render custom control +### Rendering custom control -To render custom control use the `renderControl` property. -Notice: you should forward all arguments to your node in order to have consistent behavior as in the case of using default control. +To render a custom control, use the `renderControl` property. +Note: You should forward all arguments to your node in order to enable consistent behavior, just as when using the default control. -### Render custom filter section +### Rendering custom filter section -To render custom filter section use the `renderFilter` property and set `filterable` property to `true`. -Notice: you should forward all arguments to your node in order to have properly working filter as in the case of using default. +To render a custom filter section, use the `renderFilter` property and set the `filterable` property to `true`. +Note: You need to forward all arguments to your node in order to enable a properly working filter, just as when using the default configuration. -### Render custom options +### Rendering custom options -To render custom options use the `renderOption` property. +To render custom options, use the `renderOption` property: -### Render custom selected options +### Rendering custom selected options -To render custom selected options use the `renderSelectedOption` property. +To render custom selected options, use the `renderSelectedOption` property: -### Render options with different heights +### Rendering options with different heights -The options have a fixed height according to the `size` property. If you need to render options with different heights, you can use the `option.data` property, which will store information about what height you need to set the options and `getOptionHeight` property to set this value. +Options have a fixed height according to the `size` property. If you need to render options with different heights, you can use the `option.data` property. It will store information about what height you need to set for the options, as well as the `getOptionHeight` property to set this value. -### Render custom popup +### Rendering custom popup -To render custom popup use the `renderPopup` property. +To render custom popup, use the `renderPopup` property. ## Properties -| Name | Description | Type | Default | -| :-------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- | -| className | Control className | `string` | | -| defaultValue | Default values that represent selected options in case of using uncontrolled state | `string[]` | | -| disabled | Indicates that the user cannot interact with the control | `boolean` | `false` | -| [filterable](#filtering-options) | Indicates that select popup have filter section | `boolean` | `false` | -| filterOption | Used to compare option with filter | `function` | | -| filterPlaceholder | Default filter input placeholder text | `string` | | -| [getOptionHeight](#render-options-with-different-heights) | Used to set height of customized user options | `function` | | -| getOptionGroupHeight | Used to set height of customized user option group | `function` | | -| hasClear | Enable displaying icon for clear selected options | `boolean` | `false` | -| id | HTML `id` attribute | `string` | | -| label | Control label | `string` | | -| loading | Add the loading item to the end of the options list. Works like persistant loading indicator while the options list is empty. | `boolean` | | -| [multiple](#selecting-multiple-options) | Indicates that multiple options can be selected in the list | `boolean` | `false` | -| name | Name of the control | `string` | | -| onBlur | Handler that is called when the element loses focus. | `function` | | -| filter | Controlled filter value | `string` | `''` | -| onFilterChange | Fires every time after changing filter | `function` | | -| onFocus | Handler that is called when the element receives focus. | `function` | | -| onLoadMore | Fires when loading indicator gets visible. | `function` | | -| onOpenChange | Fires every time after changing popup visibility | `function` | | -| onUpdate | Fires when an alteration to the Select value is committed by the user | `function` | | -| [options](#options) | Options to select | `(SelectOption \| SelectOptionGroup)[]` | | -| pin | Control border view | `string` | `'round-round'` | -| placeholder | Placeholder text | `string` | | -| popupClassName | Popup with options list className | `string` | | -| popupPlacement | `Popper.js` placement | `PopupPlacement` `Array` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` | -| [popupWidth](#popup-width) | Popup width | `number \| 'fit' \| 'outfit'` | `'outfit'` | -| qa | Test id attribute (`data-qa`) | `string` | | -| [renderControl](#render-custom-control) | Used to render user control | `function` | | -| renderEmptyOptions | Used to render node for an empty options list | `function` | | -| [renderFilter](#render-custom-filter-section) | Used to render user filter section | `function` | | -| [renderOption](#render-custom-options) | Used to render user options | `function` | | -| renderOptionGroup | Used to render user option groups | `function` | | -| [renderSelectedOption](#render-custom-selected-options) | Used to render user selected options | `function` | | -| [renderPopup](#render-custom-popup) | Used to render user popup content | `function` | | -| [size](#size) | Control / options size | `string` | `'m'` | -| value | Values that represent selected options | `string[]` | | -| view | Control view | `string` | `'normal'` | -| [virtualizationThreshold](#virtualized-list) | The threshold of the options count after which virtualization is enabled | `number` | `50` | -| [width](#control-width) | Control width | `string \| number` | `undefined` | -| errorMessage | Error text | `string` | | -| errorPlacement | Error placement | `outside` `inside` | `outside` | -| validationState | Validation state | `"invalid"` | | -| [hasCounter](#counter) | Indicates count of the selected options. Counter appears only when [multiple](#selecting-multiple-options) selection enabled. state | `boolean` | +| Name | Description | Type | Default | +| :-------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- | +| className | Control className | `string` | | +| defaultValue | Default values that represent selected options in case of using an uncontrolled state | `string[]` | | +| disabled | Shows that the user cannot work with the control | `boolean` | `false` | +| [filterable](#filtering-options) | Shows that select popup has a filter section | `boolean` | `false` | +| filterOption | Used to compare option with filter | `function` | | +| filterPlaceholder | Default filter input placeholder text | `string` | | +| [getOptionHeight](#render-options-with-different-heights) | Used to set height of customized user options | `function` | | +| getOptionGroupHeight | Used to set height of customized user option group | `function` | | +| hasClear | Enables displaying icon for clearing selected options | `boolean` | `false` | +| id | `id` HTML attribute | `string` | | +| label | Control label | `string` | | +| loading | Adds the loading item to the end of the option list. Works like a persistent loading indicator while the options list is empty. | `boolean` | | +| [multiple](#selecting-multiple-options) | Shows whether multiple options can be selected in the list | `boolean` | `false` | +| name | Name of the control | `string` | | +| onBlur | Handler that is called when the element loses focus. | `function` | | +| filter | Controlled filter value | `string` | `''` | +| onFilterChange | Fires every time after changing the filter | `function` | | +| onFocus | Handler that is called when the element gets focus | `function` | | +| onLoadMore | Fires when the loading indicator gets visible | `function` | | +| onOpenChange | Fires every time after changing popup visibility | `function` | | +| onUpdate | Fires when an alteration to the `Select` value is committed by the user | `function` | | +| [options](#options) | Options to select | `(SelectOption \| SelectOptionGroup)[]` | | +| pin | Control border view | `string` | `'round-round'` | +| placeholder | Placeholder text | `string` | | +| popupClassName | Popup with the option list `className` | `string` | | +| popupPlacement | `Popper.js` placement | `PopupPlacement` `Array` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` | +| [popupWidth](#popup-width) | Popup width | `number \| 'fit' \| 'outfit'` | `'outfit'` | +| qa | Test id attribute (`data-qa`) | `string` | | +| [renderControl](#render-custom-control) | Used to render user control | `function` | | +| renderEmptyOptions | Used to render a node for an empty option list | `function` | | +| [renderFilter](#render-custom-filter-section) | Used to render user filter section | `function` | | +| [renderOption](#render-custom-options) | Used to render user options | `function` | | +| renderOptionGroup | Used to render user option groups | `function` | | +| [renderSelectedOption](#render-custom-selected-options) | Used to render user selected options | `function` | | +| [renderPopup](#render-custom-popup) | Used to render user popup content | `function` | | +| [size](#size) | Control / options size | `string` | `'m'` | +| value | Values that represent selected options | `string[]` | | +| view | Control view | `string` | `'normal'` | +| [virtualizationThreshold](#virtualized-list) | Option count threshold after which virtualization is enabled | `number` | `50` | +| [width](#control-width) | Control width | `string \| number` | `undefined` | +| errorMessage | Error text | `string` | | +| errorPlacement | Error position | `outside` `inside` | `outside` | +| validationState | Validation state | `"invalid"` | | +| [hasCounter](#counter) | Shows the selected option count. The counter appears only when the [multiple](#selecting-multiple-options) selection is enabled. | `boolean` | ## CSS API -| Name | Description | -| :------------------------------- | :-------------------------------------------------- | -| `--g-select-focus-outline-color` | Outline color if focused (by default not presented) | +| Name | Description | +| :------------------------------- | :-------------------------------------------- | +| `--g-select-focus-outline-color` | Outline color if focused (missing by default) | diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx index 22dba73d51..392d046d16 100644 --- a/src/components/Select/Select.tsx +++ b/src/components/Select/Select.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {KeyCode} from '../../constants'; import {useControlledState, useFocusWithin, useForkRef, useSelect, useUniqId} from '../../hooks'; diff --git a/src/components/Select/__stories__/Select.stories.tsx b/src/components/Select/__stories__/Select.stories.tsx index 2a2588623e..29ba95f5e7 100644 --- a/src/components/Select/__stories__/Select.stories.tsx +++ b/src/components/Select/__stories__/Select.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; import {Select} from '..'; diff --git a/src/components/Select/__stories__/SelectPopupWidthShowcase.tsx b/src/components/Select/__stories__/SelectPopupWidthShowcase.tsx index e2745df246..7055931c1c 100644 --- a/src/components/Select/__stories__/SelectPopupWidthShowcase.tsx +++ b/src/components/Select/__stories__/SelectPopupWidthShowcase.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Text} from '../../Text/Text'; import {Flex} from '../../layout/Flex/Flex'; diff --git a/src/components/Select/__stories__/SelectShowcase.tsx b/src/components/Select/__stories__/SelectShowcase.tsx index 2ce69cb6d0..6ced9084ee 100644 --- a/src/components/Select/__stories__/SelectShowcase.tsx +++ b/src/components/Select/__stories__/SelectShowcase.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Plus, TrashBin} from '@gravity-ui/icons'; import range from 'lodash/range'; diff --git a/src/components/Select/__stories__/UseSelectOptionsShowcase.tsx b/src/components/Select/__stories__/UseSelectOptionsShowcase.tsx index 36902cb42f..6ed5bee123 100644 --- a/src/components/Select/__stories__/UseSelectOptionsShowcase.tsx +++ b/src/components/Select/__stories__/UseSelectOptionsShowcase.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Button} from '../../Button'; import {TextInput} from '../../controls'; diff --git a/src/components/Select/__tests__/Select.base-actions.test.tsx b/src/components/Select/__tests__/Select.base-actions.test.tsx index 5cd3a2f4fe..9fb9e1b981 100644 --- a/src/components/Select/__tests__/Select.base-actions.test.tsx +++ b/src/components/Select/__tests__/Select.base-actions.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Select/__tests__/Select.error.test.tsx b/src/components/Select/__tests__/Select.error.test.tsx index 372627ee03..f94909c467 100644 --- a/src/components/Select/__tests__/Select.error.test.tsx +++ b/src/components/Select/__tests__/Select.error.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {render, screen} from '../../../../test-utils/utils'; import {CONTROL_ERROR_MESSAGE_QA} from '../../controls/utils'; import {Select} from '../Select'; diff --git a/src/components/Select/__tests__/Select.filter.test.tsx b/src/components/Select/__tests__/Select.filter.test.tsx index 8bbda16feb..e323be2d3e 100644 --- a/src/components/Select/__tests__/Select.filter.test.tsx +++ b/src/components/Select/__tests__/Select.filter.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Select/__tests__/Select.form.test.tsx b/src/components/Select/__tests__/Select.form.test.tsx index 9aa60655b4..3e7dfc05b1 100644 --- a/src/components/Select/__tests__/Select.form.test.tsx +++ b/src/components/Select/__tests__/Select.form.test.tsx @@ -1,5 +1,5 @@ /* eslint-disable testing-library/no-node-access */ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Select/__tests__/Select.renderPopup.test.tsx b/src/components/Select/__tests__/Select.renderPopup.test.tsx index 482c8447a8..9425aa42ba 100644 --- a/src/components/Select/__tests__/Select.renderPopup.test.tsx +++ b/src/components/Select/__tests__/Select.renderPopup.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Select/__tests__/utils.tsx b/src/components/Select/__tests__/utils.tsx index 1e31b82b7f..59f8c75a1d 100644 --- a/src/components/Select/__tests__/utils.tsx +++ b/src/components/Select/__tests__/utils.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import range from 'lodash/range'; diff --git a/src/components/Select/components/EmptyOptions/EmptyOptions.tsx b/src/components/Select/components/EmptyOptions/EmptyOptions.tsx index 413ee6b14f..f8ba656d41 100644 --- a/src/components/Select/components/EmptyOptions/EmptyOptions.tsx +++ b/src/components/Select/components/EmptyOptions/EmptyOptions.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {block} from '../../../utils/cn'; import type {SelectProps} from '../../types'; diff --git a/src/components/Select/components/HiddenSelect/HiddenSelect.tsx b/src/components/Select/components/HiddenSelect/HiddenSelect.tsx index 30d96ac545..7a2f9134c0 100644 --- a/src/components/Select/components/HiddenSelect/HiddenSelect.tsx +++ b/src/components/Select/components/HiddenSelect/HiddenSelect.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useFormResetHandler} from '../../../../hooks/private'; diff --git a/src/components/Select/components/SelectClear/SelectClear.tsx b/src/components/Select/components/SelectClear/SelectClear.tsx index 93347b583f..c22d5ed3ff 100644 --- a/src/components/Select/components/SelectClear/SelectClear.tsx +++ b/src/components/Select/components/SelectClear/SelectClear.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Xmark} from '@gravity-ui/icons'; import {Icon} from '../../../Icon'; diff --git a/src/components/Select/components/SelectControl/SelectControl.tsx b/src/components/Select/components/SelectControl/SelectControl.tsx index 8b764280a6..e58ec2cfa6 100644 --- a/src/components/Select/components/SelectControl/SelectControl.tsx +++ b/src/components/Select/components/SelectControl/SelectControl.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {ChevronDown, TriangleExclamation} from '@gravity-ui/icons'; import isEmpty from 'lodash/isEmpty'; diff --git a/src/components/Select/components/SelectCounter/SelectCounter.tsx b/src/components/Select/components/SelectCounter/SelectCounter.tsx index 4e1932b62e..86857e91d2 100644 --- a/src/components/Select/components/SelectCounter/SelectCounter.tsx +++ b/src/components/Select/components/SelectCounter/SelectCounter.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Text} from '../../../Text'; import {block} from '../../../utils/cn'; import type {SelectCounterProps} from '../../types'; diff --git a/src/components/Select/components/SelectFilter/SelectFilter.tsx b/src/components/Select/components/SelectFilter/SelectFilter.tsx index fbfb9e2898..827b14cca2 100644 --- a/src/components/Select/components/SelectFilter/SelectFilter.tsx +++ b/src/components/Select/components/SelectFilter/SelectFilter.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {TextInput} from '../../../controls'; import {block} from '../../../utils/cn'; diff --git a/src/components/Select/components/SelectList/GroupLabel.tsx b/src/components/Select/components/SelectList/GroupLabel.tsx index b1464daaf5..849291b02b 100644 --- a/src/components/Select/components/SelectList/GroupLabel.tsx +++ b/src/components/Select/components/SelectList/GroupLabel.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import {block} from '../../../utils/cn'; import type {GroupTitleItem} from '../../utils'; diff --git a/src/components/Select/components/SelectList/OptionWrap.tsx b/src/components/Select/components/SelectList/OptionWrap.tsx index 054aa99f8f..e3b6708e65 100644 --- a/src/components/Select/components/SelectList/OptionWrap.tsx +++ b/src/components/Select/components/SelectList/OptionWrap.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import {Check} from '@gravity-ui/icons'; @@ -20,8 +20,12 @@ type OptionWrapProps = { }; const DefaultOption = ({option}: DefaultOptionProps) => { - const {content, children, disabled} = option; - return {content || children}; + const {content, children, disabled, title} = option; + return ( + + {content || children} + + ); }; export const OptionWrap = (props: OptionWrapProps) => { diff --git a/src/components/Select/components/SelectList/SelectList.tsx b/src/components/Select/components/SelectList/SelectList.tsx index 5d2e383918..225529b1be 100644 --- a/src/components/Select/components/SelectList/SelectList.tsx +++ b/src/components/Select/components/SelectList/SelectList.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {List} from '../../../List'; import {SelectQa, selectListBlock} from '../../constants'; diff --git a/src/components/Select/components/SelectList/SelectLoadingIndicator.tsx b/src/components/Select/components/SelectList/SelectLoadingIndicator.tsx index e23fedddd6..104fd6653c 100644 --- a/src/components/Select/components/SelectList/SelectLoadingIndicator.tsx +++ b/src/components/Select/components/SelectList/SelectLoadingIndicator.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useIntersection} from '../../../../hooks'; import {Loader} from '../../../Loader/Loader'; diff --git a/src/components/Select/components/SelectPopup/SelectPopup.tsx b/src/components/Select/components/SelectPopup/SelectPopup.tsx index f212de4e63..925a0eb9c1 100644 --- a/src/components/Select/components/SelectPopup/SelectPopup.tsx +++ b/src/components/Select/components/SelectPopup/SelectPopup.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import type {PopperPlacement} from '../../../../hooks/private'; import {Popup} from '../../../Popup'; diff --git a/src/components/Select/components/SelectPopup/types.ts b/src/components/Select/components/SelectPopup/types.ts index 597d754261..2925047a29 100644 --- a/src/components/Select/components/SelectPopup/types.ts +++ b/src/components/Select/components/SelectPopup/types.ts @@ -1,4 +1,4 @@ -import type React from 'react'; +import type * as React from 'react'; import type {PopupPlacement} from '../../../Popup'; import type {SelectProps} from '../../types'; diff --git a/src/components/Select/hooks-public/useSelectOptions/index.ts b/src/components/Select/hooks-public/useSelectOptions/index.ts index 91ea9ad2dd..042c99f828 100644 --- a/src/components/Select/hooks-public/useSelectOptions/index.ts +++ b/src/components/Select/hooks-public/useSelectOptions/index.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import get from 'lodash/get'; diff --git a/src/components/Select/hooks/useActiveItemIndex.ts b/src/components/Select/hooks/useActiveItemIndex.ts index 82f3410bcc..5a691351c9 100644 --- a/src/components/Select/hooks/useActiveItemIndex.ts +++ b/src/components/Select/hooks/useActiveItemIndex.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {FlattenOption} from '../utils'; diff --git a/src/components/Select/hooks/useQuickSearch.ts b/src/components/Select/hooks/useQuickSearch.ts index 8974fc5062..9e65542e70 100644 --- a/src/components/Select/hooks/useQuickSearch.ts +++ b/src/components/Select/hooks/useQuickSearch.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {QUICK_SEARCH_TIMEOUT} from '../constants'; import {getNextQuickSearch} from '../utils'; diff --git a/src/components/Select/types.ts b/src/components/Select/types.ts index 7f88949748..2a8347a641 100644 --- a/src/components/Select/types.ts +++ b/src/components/Select/types.ts @@ -1,4 +1,4 @@ -import type React from 'react'; +import type * as React from 'react'; import type {PopperPlacement} from '../../hooks/private'; import type {UseOpenProps} from '../../hooks/useSelect/types'; @@ -176,6 +176,7 @@ export type SelectOptionGroup = { /** Label is a string which displayed above the options group. * If label is empty string, group item height will be 0 and only border will be displayed */ label: string; + data?: T; options?: SelectOption[]; children?: | React.ReactElement diff --git a/src/components/Select/utils.tsx b/src/components/Select/utils.tsx index a0f7e8d9bb..0f5c19d6cb 100644 --- a/src/components/Select/utils.tsx +++ b/src/components/Select/utils.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {KeyCode} from '../../constants'; import type {List, ListItemData} from '../List'; @@ -19,7 +19,7 @@ import type { } from './types'; // "disable" property needs to deactivate group title item in List -export type GroupTitleItem = {label: string; disabled: true}; +export type GroupTitleItem = {label: string; disabled: true; data?: T}; export type FlattenOption = SelectOption | GroupTitleItem; @@ -38,7 +38,7 @@ export const isSelectGroupTitle = ( export const getFlattenOptions = (options: SelectOptions): FlattenOptions => { const flatten = options.reduce((acc, option) => { if ('label' in option) { - acc.push({label: option.label, disabled: true}); + acc.push({label: option.label, disabled: true, data: option.data}); acc.push(...(option.options || [])); } else { acc.push(option); diff --git a/src/components/Sheet/README-ru.md b/src/components/Sheet/README-ru.md new file mode 100644 index 0000000000..212ad70e91 --- /dev/null +++ b/src/components/Sheet/README-ru.md @@ -0,0 +1,53 @@ + + +# Sheet + + + +```tsx +import {Sheet} from '@gravity-ui/uikit'; +``` + +Компонент `Sheet` (шторка) предназначен для использования в мобильных интерфейсах в качестве информационного или интерактивного элемента. Благодаря поддержке внутренней прокрутки и динамического изменения размеров в него можно помещать контент любого объема. + +На мобильных устройствах `Sheet` можно перемещать, потянув за его основную часть или область свайпа. Для закрытия нужно провести вниз или коснуться области вне `Sheet`. + +## Использование + +```tsx +import React from 'react'; +import {Button, Sheet} from '@gravity-ui/uikit'; + +const SheetExample = () => { + const [visible, setVisible] = React.useState(false); + + return ( + + + setVisible(false)} title="Content Sheet"> + Content + + + ); +}; +``` + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------- | :--------: | :-------------------: | +| visible | Управляет видимостью `Sheet`. | `boolean` | `false` | +| allowHideOnContentScroll | Включает возможность закрытия при свайпе вниз, если контент не прокручивается или прокручен до верха (`content Node.scrollTop === 0`). | `boolean` | `true` | +| hideTopBar | Скрывает верхнюю панель с элементом для изменения размера. | `boolean` | | +| id | Идентификатор `Sheet`, используемый как хеш в URL. Необходимо задать разные значения `id`, если на странице несколько `Sheet`. | `string` | `modal` | +| title | Заголовок окна `Sheet`. | `string` | `undefined` | +| className | HTML-атрибут `class`. | `string` | `undefined` | +| contentClassName | HTML-атрибут `class` для контента шторки. | `string` | `undefined` | +| swipeAreaClassName | HTML-атрибут `class` для области свайпа. | `string` | `undefined` | +| onClose | Обработчик события закрытия. | `function` | `undefined` | + +## API CSS + +| Имя | Описание | +| :-------------------------- | :---------------- | +| `--g-sheet-content-padding` | Отступы контента. | diff --git a/src/components/Sheet/README.md b/src/components/Sheet/README.md index 5f158767be..8f2e517dd8 100644 --- a/src/components/Sheet/README.md +++ b/src/components/Sheet/README.md @@ -8,9 +8,9 @@ import {Sheet} from '@gravity-ui/uikit'; ``` -`Sheet` is a component designed to be used in a mobile context as an information or interactive element. You can place content of any size in it - internal scrolling and dynamic resizing are supported. +`Sheet` is a component designed for using in the mobile context as an information or interactive element. You can place content of any size in it, since the internal scrolling and dynamic resizing are supported. -On mobile devices, you can move `Sheet` by pulling on its main part or the swipe area. To close it, swipe down or touch the area outside the `Sheet`. +On mobile devices, you can move a `Sheet` by pulling its main part or the swipe area. To close it, swipe down or tap the area outside the `Sheet`. ## Usage @@ -34,17 +34,17 @@ const SheetExample = () => { ## Properties -| Name | Description | Type | Default | -| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------: | :---------: | -| visible | Manages `Sheet` visibility | `boolean` | `false` | -| allowHideOnContentScroll | Enable the behavior of the sheet window closing by swiping down if the content is scrolled to its top (`content Node.scrollTop === 0`) or has no scroll at all | `boolean` | `true` | -| hideTopBar | Hide top bar with resize handle | `boolean` | | -| id | ID of the sheet, used as hash in URL. It's important to specify different `id` values if there can be more than one sheet on the page | `string` | `modal` | -| title | Title of the sheet window | `string` | `undefined` | -| className | HTML `class` attribute | `string` | `undefined` | -| contentClassName | HTML `class` attribute for the sheet content | `string` | `undefined` | -| swipeAreaClassName | HTML `class` attribute for the swipe area | `string` | `undefined` | -| onClose | Handler for close event | `function` | `undefined` | +| Name | Description | Type | Default | +| :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------: | :---------: | +| visible | Manages `Sheet` visibility | `boolean` | `false` | +| allowHideOnContentScroll | Enables the behavior of closing the sheet window by swiping down if the content is scrolled to its top (`content Node.scrollTop === 0`) or has no scroll at all. | `boolean` | `true` | +| hideTopBar | Hides the top bar with the resize handle. | `boolean` | | +| id | Sheet ID used as hash in a URL. Make sure to specify multiple `id` values if there can be more than one sheet on a page. | `string` | `modal` | +| title | Sheet window title. | `string` | `undefined` | +| className | `class` HTML attribute | `string` | `undefined` | +| contentClassName | `class` HTML attribute for the sheet content. | `string` | `undefined` | +| swipeAreaClassName | `class` HTML attribute for the swipe area. | `string` | `undefined` | +| onClose | Handler for close event. | `function` | `undefined` | ## CSS API diff --git a/src/components/Sheet/Sheet.tsx b/src/components/Sheet/Sheet.tsx index 3cf14bd329..a23ac820ca 100644 --- a/src/components/Sheet/Sheet.tsx +++ b/src/components/Sheet/Sheet.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useBodyScrollLock} from '../../hooks'; import {Portal} from '../Portal/Portal'; diff --git a/src/components/Sheet/SheetContent.tsx b/src/components/Sheet/SheetContent.tsx index eb93933c44..82eafc96eb 100644 --- a/src/components/Sheet/SheetContent.tsx +++ b/src/components/Sheet/SheetContent.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Platform, withMobile} from '../mobile'; import type {History, Location, MobileContextProps} from '../mobile'; @@ -16,7 +16,7 @@ const ACCELERATION_Y_MAX = 0.08; const ACCELERATION_Y_MIN = -0.02; // 90% from viewport const MAX_CONTENT_HEIGHT_FROM_VIEWPORT_COEFFICIENT = 0.9; -const WINDOW_RESIZE_TIMEOUT = 25; +const WINDOW_RESIZE_TIMEOUT = 50; let hashHistory: string[] = []; @@ -123,21 +123,14 @@ class SheetContent extends React.Component { + if (this.state.isAnimating) { + return; + } + this.setState({inWindowResizeScope: true}); if (this.resizeWindowTimer) { diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-chromium-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-chromium-linux.png index 21ceb9988d..5decf1c226 100644 Binary files a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-chromium-linux.png and b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-chromium-linux.png differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-webkit-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-webkit-linux.png deleted file mode 100644 index 34fe049d7e..0000000000 Binary files a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-chromium-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-chromium-linux.png index 197488bf20..90bf2da409 100644 Binary files a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-chromium-linux.png and b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-chromium-linux.png differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-webkit-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-webkit-linux.png deleted file mode 100644 index 8d3788f05f..0000000000 Binary files a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-render-story-Default-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-default-light-chromium-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-default-light-chromium-linux.png new file mode 100644 index 0000000000..b5111cc242 Binary files /dev/null and b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-default-light-chromium-linux.png differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-hideTopBar-true-light-chromium-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-hideTopBar-true-light-chromium-linux.png new file mode 100644 index 0000000000..d1992b4672 Binary files /dev/null and b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-hideTopBar-true-light-chromium-linux.png differ diff --git a/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-title-Title-light-chromium-linux.png b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-title-Title-light-chromium-linux.png new file mode 100644 index 0000000000..08ad274932 Binary files /dev/null and b/src/components/Sheet/__snapshots__/Sheet.visual.test.tsx-snapshots/Sheet-smoke-title-Title-light-chromium-linux.png differ diff --git a/src/components/Sheet/__stories__/DefaultShowcase/DefaultShowcase.stories.tsx b/src/components/Sheet/__stories__/DefaultShowcase/DefaultShowcase.stories.tsx index 7b0f1bde4a..ab4866ad1f 100644 --- a/src/components/Sheet/__stories__/DefaultShowcase/DefaultShowcase.stories.tsx +++ b/src/components/Sheet/__stories__/DefaultShowcase/DefaultShowcase.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/Sheet/__stories__/MultipleSheetsShowcase/MultipleSheets.stories.tsx b/src/components/Sheet/__stories__/MultipleSheetsShowcase/MultipleSheets.stories.tsx index 085efe501f..9a9a9d4f13 100644 --- a/src/components/Sheet/__stories__/MultipleSheetsShowcase/MultipleSheets.stories.tsx +++ b/src/components/Sheet/__stories__/MultipleSheetsShowcase/MultipleSheets.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/Sheet/__stories__/WithMenuShowcase/WithMenuShowcase.stories.tsx b/src/components/Sheet/__stories__/WithMenuShowcase/WithMenuShowcase.stories.tsx index 8462ddc7c8..2ccf410bef 100644 --- a/src/components/Sheet/__stories__/WithMenuShowcase/WithMenuShowcase.stories.tsx +++ b/src/components/Sheet/__stories__/WithMenuShowcase/WithMenuShowcase.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {Meta, StoryFn} from '@storybook/react'; diff --git a/src/components/Sheet/__tests__/Sheet.test.tsx b/src/components/Sheet/__tests__/Sheet.test.tsx index 7223d0395a..c100fea62b 100644 --- a/src/components/Sheet/__tests__/Sheet.test.tsx +++ b/src/components/Sheet/__tests__/Sheet.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {render, screen} from '../../../../test-utils/utils'; import {Sheet} from '../Sheet'; import {sheetBlock} from '../constants'; diff --git a/src/components/Sheet/__tests__/Sheet.visual.test.tsx b/src/components/Sheet/__tests__/Sheet.visual.test.tsx index dd3192f7f8..591cdb36eb 100644 --- a/src/components/Sheet/__tests__/Sheet.visual.test.tsx +++ b/src/components/Sheet/__tests__/Sheet.visual.test.tsx @@ -1,14 +1,15 @@ -import React from 'react'; - -import {expect} from '@playwright/test'; - -import {test} from '~playwright/core'; +import {expect, smokeTest, test} from '~playwright/core'; +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {SheetProps} from '../Sheet'; import {DEFAULT_SHEET_QA} from '../__stories__/constants'; +import {hideTopBarCases, titleCases} from './cases'; +import {QASheet} from './constants'; +import {TestSheet} from './helpers'; import {SheetStories} from './helpersPlaywright'; -test.describe('Sheet', () => { +test.describe('Sheet', {tag: '@Sheet'}, () => { test('render story: ', async ({page, mount, expectScreenshot}) => { await mount(); @@ -23,4 +24,31 @@ test.describe('Sheet', () => { component: sheetLocator, }); }); + + createSmokeScenarios>>( + {}, + { + hideTopBar: hideTopBarCases, + title: titleCases, + }, + ).forEach(([title, props]) => { + smokeTest(title, async ({mount, page, expectScreenshot}) => { + await page.setViewportSize({width: 500, height: 500}); + + const root = await mount(, { + rootStyle: { + padding: 0, + width: '100%', + minHeight: '500px', + }, + }); + + await root.locator('button').click(); + await expect(page.locator(`[data-qa='${QASheet.content}']`)).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + }); }); diff --git a/src/components/Sheet/__tests__/cases.tsx b/src/components/Sheet/__tests__/cases.tsx new file mode 100644 index 0000000000..1ddc68872e --- /dev/null +++ b/src/components/Sheet/__tests__/cases.tsx @@ -0,0 +1,5 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {SheetProps} from '../Sheet'; + +export const hideTopBarCases: Cases = [true]; +export const titleCases: Cases = ['Title']; diff --git a/src/components/Sheet/__tests__/constants.ts b/src/components/Sheet/__tests__/constants.ts new file mode 100644 index 0000000000..2409753b01 --- /dev/null +++ b/src/components/Sheet/__tests__/constants.ts @@ -0,0 +1,3 @@ +export const QASheet = { + content: 'content', +}; diff --git a/src/components/Sheet/__tests__/helpers.tsx b/src/components/Sheet/__tests__/helpers.tsx new file mode 100644 index 0000000000..205d981105 --- /dev/null +++ b/src/components/Sheet/__tests__/helpers.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; + +import type {SheetProps} from '../Sheet'; +import {Sheet} from '../Sheet'; + +import {QASheet} from './constants'; + +export const TestSheet = (props: Partial>) => { + const [visible, setVisible] = React.useState(false); + + return ( +
+ + setVisible(false)} + qa={QASheet.content} + > +
+ Sheet content +
+
+
+ ); +}; diff --git a/src/components/Skeleton/README-ru.md b/src/components/Skeleton/README-ru.md new file mode 100644 index 0000000000..5de4b74a4f --- /dev/null +++ b/src/components/Skeleton/README-ru.md @@ -0,0 +1,19 @@ + + +# Skeleton + + + +```tsx +import {Skeleton} from '@gravity-ui/uikit'; +``` + +Компонент `Skeleton` отображает временный макет контента, пока фактические данные загружаются. Такой предварительный макет используется для снижения раздражения, вызываемого ожиданием загрузки. + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :----------------------------------------------------- | :-------------------: | :-------------------: | +| style | Пользовательские CSS-стили корневого элемента. | `React.CSSProperties` | | +| className | Пользовательский CSS-класс корневого элемента. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | diff --git a/src/components/Skeleton/README.md b/src/components/Skeleton/README.md index b861d20ba5..5c2e00922e 100644 --- a/src/components/Skeleton/README.md +++ b/src/components/Skeleton/README.md @@ -8,12 +8,12 @@ import {Skeleton} from '@gravity-ui/uikit'; ``` -The Skeleton component displays a placeholder preview of your content before the data loads in order to reduce load-time frustration. +The Skeleton component displays a placeholder preview of your content before the data gets loaded. This preview is shown in order to reduce the loading time frustration. ## Properties -| Name | Description | Type | Default | -| :-------- | :-------------------------------------- | :-------------------: | :-----: | -| style | Custom CSS properties for root element | `React.CSSProperties` | | -| className | Custom CSS class for root element | `string` | | -| qa | HTML `data-qa` attribute, used in tests | `string` | | +| Name | Description | Type | Default | +| :-------- | :----------------------------------------- | :-------------------: | :-----: | +| style | Custom CSS properties for root element | `React.CSSProperties` | | +| className | Custom CSS class for the root element | `string` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | diff --git a/src/components/Skeleton/Skeleton.tsx b/src/components/Skeleton/Skeleton.tsx index 1f5eb0f168..db652459a1 100644 --- a/src/components/Skeleton/Skeleton.tsx +++ b/src/components/Skeleton/Skeleton.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import type {QAProps} from '../types'; import {block} from '../utils/cn'; diff --git a/src/components/Skeleton/__stories__/Skeleton.stories.tsx b/src/components/Skeleton/__stories__/Skeleton.stories.tsx index 660c76a19e..6452e076fe 100644 --- a/src/components/Skeleton/__stories__/Skeleton.stories.tsx +++ b/src/components/Skeleton/__stories__/Skeleton.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryFn} from '@storybook/react'; import {Skeleton} from '../Skeleton'; diff --git a/src/components/Skeleton/__stories__/SkeletonShowcase.tsx b/src/components/Skeleton/__stories__/SkeletonShowcase.tsx index a15ada4012..6bb0dd93a2 100644 --- a/src/components/Skeleton/__stories__/SkeletonShowcase.tsx +++ b/src/components/Skeleton/__stories__/SkeletonShowcase.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Button} from '../../Button'; import {cn} from '../../utils/cn'; diff --git a/src/components/Slider/BaseSlider/BaseSlider.tsx b/src/components/Slider/BaseSlider/BaseSlider.tsx index ef18676dd2..1076e37930 100644 --- a/src/components/Slider/BaseSlider/BaseSlider.tsx +++ b/src/components/Slider/BaseSlider/BaseSlider.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import Slider from 'rc-slider'; import type {SliderProps, SliderRef} from 'rc-slider'; diff --git a/src/components/Slider/HandleWithTooltip/HandleWithTooltip.tsx b/src/components/Slider/HandleWithTooltip/HandleWithTooltip.tsx index ac9c80fe03..3bb9ded181 100644 --- a/src/components/Slider/HandleWithTooltip/HandleWithTooltip.tsx +++ b/src/components/Slider/HandleWithTooltip/HandleWithTooltip.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {SliderTooltip} from '../SliderTooltip/SliderTooltip'; import type {HandleWithTooltipProps} from '../types'; diff --git a/src/components/Slider/README-ru.md b/src/components/Slider/README-ru.md new file mode 100644 index 0000000000..a625607127 --- /dev/null +++ b/src/components/Slider/README-ru.md @@ -0,0 +1,332 @@ + + +# Slider + + + +```tsx +import {Slider} from '@gravity-ui/uikit'; +``` + +`Slider` (слайдер) — это настраиваемый и отзывчивый React-компонент, который позволяет пользователям выбирать одно значение или диапазон значений из заданного набора данных. + +## Варианты слайдера + +### Одиночный слайдер + +Представляет собой слайдер с одним ползунком для выбора одного значения. Используется по умолчанию. + + + + + +```tsx + +``` + + + +### Слайдер диапазона + +Представляет собой слайдер с двумя ползунками для выбора диапазона. Для его использования необходимо задать `defaultValue` (для неконтролируемого компонента) или `value` (для контролируемого компонента) в виде массива. + + + + + +```tsx + +``` + + + +## Состояния + +### `Disabled` (отключен) + +Состояние `Slider`, при котором пользователь не может взаимодействовать с компонентом. + + + + + +```tsx + +``` + + + +### `Error` (ошибка) + +Состояние `Slider`, которое указывает на некорректный ввод данных пользователем. Для изменения внешнего представления `Slider` примените свойство `validationState`, задав ему значение `"invalid"`. Дополнительно через свойство `errorMessage` можно добавить текст сообщения, который будет отображаться под компонентом. + + + + + +```tsx + + +``` + + + +## Размер + +Для изменения размера `Slider` используйте свойство `size`. Размер по умолчанию — `m`. + + + + + +```tsx + + + + +``` + + + +## Значение + +### Минимальное и максимальное значения + +Свойства `min` и `max` определяют пределы диапазона, который может обрабатывать `Slider`. Эти свойства необходимы для установки границ выбираемых значений. + + + + + +```tsx + + + +``` + + + +### `Step` (шаг) + +Свойство `step` компонента `Slider` задает величину шага между минимальным и максимальным значениями. Оно контролирует изменение значения при перемещении ползунка. + + + + + +```tsx + +``` + + + +### Метки + +Свойство `marks` задает количество визуальных меток компонента `Slider`, указывающих на разные значения в диапазоне от минимума до максимума. Данное свойство делает слайдер более удобным для пользователя и улучшает его визуальное оформление, особенно в тех случаях, когда необходимо обозначить конкретные интервалы. Значение по умолчанию — 2 (`min` и `max`). Его можно использовать двумя способами: + +- Для задания количества визуальных меток на слайдере: + + + + +```tsx + +``` + + + +- Для указания массива значений меток на слайдере: + + + + + +```tsx + +``` + + + +Если в свойстве `marks` указать `0` или пустой массив (`[]`), то все метки компонента `Slider` будут скрыты. + + + + + +```tsx + +``` + + + +> Значение метки можно выбрать, даже если оно не соответствует шагу (`step`). + +Формат отображения значений меток можно изменить с помощью свойства `marksFormat`. + +#### Определение доступных значений + +Установка свойства `step` в `null` позволяет задать конкретные значения, которые будут доступны на слайдере, вместо непрерывного диапазона. Это особенно полезно в случаях, когда выбор возможен только из заранее определенных дискретных значений. При такой настройке свойства `min`, `max` и `marks` позволяют задать массив чисел, представляющих собой те значения, которые пользователи могут выбрать при работе с компонентом `Slider`. + + + + + +```tsx + +``` + + + +## Тултип + +Свойство `tooltipDisplay` в компоненте `Slider` управляет поведением отображения тултипа с текущим значением при взаимодействии пользователя со слайдером. Значение `auto` позволяет отображать тултип только при наведении курсора на ползунок компонента `Slider` или получении компонентом фокуса. + + + + + +```tsx + +``` + + + +Формат отображения значения тултипа можно изменить с помощью свойства `tooltipFormat`. Если не указать `tooltipformat`, то для отображения значения в тултипе будет использовано свойство `marksFormat`. + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------: | :-------------------: | +| apiRef | Ссылка на свойства `focus` and `blur` компонента `Slider`. | `RefObject` | | +| autoFocus | Атрибут `autofocus` для контрола. | `boolean` | | +| [availableValues](#define-available-values) | Устаревшее свойство; вместо него используйте `marks` и `step === null`. Задает массив доступных значений для слайдера. | `number[]` | | +| className | Имя класса обертки контрола. | `string` | | +| debounceDelay | Устаревшее свойство; используйте внешний дебаунсинг. Определяет задержку (в миллисекундах) перед вызовом функции обработки. | `number` | `0` | +| [defaultValue](#slider-variants) | Значение по умолчанию для контрола, используемое при неконтролируемом состоянии компонента. | `number` `[number, number]` | `0` | +| [disabled](#disabled) | Указывает на то, что пользователь не может взаимодействовать с контролом. | `boolean` | `false` | +| [errorMessage](#error) | Отображаемый текст ошибки. | `string` | | +| [hasTooltip](#tooltip) | Устаревшее свойство; вместо него используйте `tooltipDisplay`. Включает или отключает отображение тултипа с текущим значением компонента. | `boolean` | `false` | +| [marks](#marks) | Текстовые метки под слайдером. В данном свойстве можно задать количество меток или массив значений, для которых они должны отображаться. При указании `0` или `[]` метки не отображаются. | `number` `number[]` | `2` | +| [marksCount](#marks) | Устаревшее свойство; вместо него используйте `marks`. Количество меток под слайдером. Делит весь диапазон на равные части. Минимальное значение — `2`. Игнорируется, если задано `availablevalues` (устаревшее свойство). | `number` | `2` | +| [marksFormat](#marks) | Определяет форматирование отображаемого значения метки. | `(value: number) => string` | | +| [max](#min-and-max-value) | Максимальное значение компонента. | `number` | `100` | +| [min](#min-and-max-value) | Минимальное значение компонента. | `number` | `0` | +| onBlur | Срабатывает, когда контрол теряет фокус. Передает событие фокуса в качестве аргумента обратного вызова. | `((e: FocusEvent) => void)` | | +| onUpdate | Срабатывает, когда пользователь изменяет значение слайдера. Передает событие изменения в качестве аргумента обратного вызова. | `((value: number \| [number, number]) => void)` | | +| onUpdateComplete | Активируется при срабатывании события `ontouchend` (завершение касания) или `onmouseup` (отпускание кнопки мыши). Передает событие изменения в качестве аргумента обратного вызова. | `((value: number \| [number, number]) => void)` | | +| onFocus | Срабатывает, когда контрол получает фокус. Передает событие фокуса в качестве аргумента обратного вызова. | `((e: FocusEvent) => void)` | | +| [size](#size) | Размер контрола. | `"s"` `"m"` `"l"` `"xl"` | `"m"` | +| [step](#step) | Величина, на которую изменяется значение слайдера при каждом перемещении ползунка. Если установить значение `null`, в качестве шагов будет использоваться свойство `marks`. Игнорируется, если задано `availablevalues` (устаревшее свойство). | `number` `null` | `1` | +| tabIndex | Атрибут `tabIndex` для контрола. | `number` `[number, number]` | | +| [tooltipDisplay](#tooltip) | Управляет поведением отображения тултипа. | `off` `on` `auto` | `off` | +| [tooltipFormat](#tooltip) | Определяет форматирование отображаемого значения тултипа. Если значение не задано, используется `marksFormat`. | `(value: number) => string` | | +| [validationState](#error) | Состояние валидации. | `"invalid"` | | +| [value](#slider-variants) | Значение контрола. | `number` `[number, number]` | | diff --git a/src/components/Slider/README.md b/src/components/Slider/README.md index 81dcf851f0..163bdbfc9f 100644 --- a/src/components/Slider/README.md +++ b/src/components/Slider/README.md @@ -8,13 +8,13 @@ import {Slider} from '@gravity-ui/uikit'; ``` -Slider is a customizable and responsive React component that allows users to select a single value or a range of values from a specified data set. +The slider is a customizable and responsive React component that allows users to select a single value or a range of values from a specified data set. -## Slider variants +## Slider variations ### Single slider -Slider with one handle to select single value. This Slider is used by default. +This is a slider with one handle to select a single value. It is used by default. ### Range slider -Slider with two handles to select range. To use this slider you should set `defaultValue` (for uncontrolled) or `value` (for controlled) to array. +This is slider with two handles to select a range. To use it, set `defaultValue` (for an uncontrolled slider) or `value` (for a controlled one) for the array. ### Disabled -The state of the `Slider` where you don't want the user to be able to interact with the component. +This is a state of a `Slider` where you do not want to allow the user to work with this component. ### Error -The state of the `Slider` in which you want to indicate incorrect user input. To change `Slider` appearance, use the `validationState` property with the `"invalid"` value. An optional message text can be added via the `errorMessage` property. Error message text will be rendered under the component. +This `Slider` state is for incorrect user input. To change the `Slider` appearance, use the `validationState` property with the `"invalid"` value. Optionally, you can provide an error message through the `errorMessage` property. This message text will be rendered under the slider. ## Size -To control the size of the `Slider` use the `size` property. Default size is `m`. +Use the `size` property to manage the `Slider` size. The default size is `m`. ## Value -### Min and max value +### Minimum and maximum value -The `min` and `max` properties define the limits of the range that the `Slider` component can handle. These properties are essential for setting the boundaries of the selectable values. +The `min` and `max` properties define the limits of the range the `Slider` can handle. These properties are essential for setting the boundaries of the selectable values. ### Step -The `step` property for `Slider` component determines the incremental steps between the min and max value range. It controls how much the value should increase or decrease as the slider is moved. +The `step` property determines the increments within the minimum and maximum value range. This means how much the value changes with a single slider move. + +# Spin + + + +```tsx +import {Spin} from '@gravity-ui/uikit'; +``` + +`Spin` — это компонент, который отображает состояние загрузки (вращающийся полукруг) в инлайн-сценариях. В отличие от `Loader`, этот компонент применяется для отображения состояния загрузки в инлайн-контексте — например, в `Button` или `Label`. + +### Размер + + + + + +```tsx + + + + + +``` + + + +`XS` — очень маленький. + +`S` — маленький, применяется, когда спин среднего размера слишком велик. + +`M` — средний (базовый), используется в большинстве случаев. + +`L` — большой, применяется, когда спин среднего размера слишком мал. + +`XL` — очень большой. + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :-------- | :--------------------------------------------- | :-----------------------------: | :-------------------: | +| size | Размер спина. | `"xs"` `"s"` `"m"` `"l"` `"xl"` | `"m"` | +| style | Пользовательские CSS-стили корневого элемента. | `React.CSSProperties` | | +| className | Пользовательский CSS-класс корневого элемента. | `string` | | +| qa | Атрибут тестирования (`data-qa`). | `string` | | diff --git a/src/components/Spin/README.md b/src/components/Spin/README.md index 089b03ef16..954c65915d 100644 --- a/src/components/Spin/README.md +++ b/src/components/Spin/README.md @@ -8,7 +8,7 @@ import {Spin} from '@gravity-ui/uikit'; ``` -The Spin component indicates the loading state (a rotating semicircle) in inline scenarios. Unlike Loader, this component is used to display the loading state in inline scenarios, e.g., in Button or Label. +The `Spin` component displays the loading state (a rotating semicircle) in inline scenarios. Unlike `Loader`, this component is used to display the loading state in inline scenarios, e.g., in a `Button` or `Label`. ### Size @@ -42,21 +42,21 @@ LANDING_BLOCK--> -XS - The smallest size. +`XS`: Extra small. -S – Small, used when standard spin is too large. +`S`: Small, used when a medium-sized spin is too large. -M – Medium (basic), used in most cases. +`M`: Medium (basic), used in most cases. -L – Large, used when standard spin is too small. +`L`: Large, used when a medium-sized spin is too small. -XL - The largest size. +`XL`: Extra large. ## Properties -| Name | Description | Type | Default | -| :-------- | :--------------------------------- | :-----------------------------: | :-----: | -| size | Spin size | `"xs"` `"s"` `"m"` `"l"` `"xl"` | `"m"` | -| style | Custom CSS styles for root element | `React.CSSProperties` | | -| className | Custom CSS class for root element | `string` | | -| qa | Test attribute (`data-qa`) | `string` | | +| Name | Description | Type | Default | +| :-------- | :------------------------------------ | :-----------------------------: | :-----: | +| size | Spin size | `"xs"` `"s"` `"m"` `"l"` `"xl"` | `"m"` | +| style | Custom CSS styles for root element | `React.CSSProperties` | | +| className | Custom CSS class for the root element | `string` | | +| qa | Test attribute (`data-qa`) | `string` | | diff --git a/src/components/Spin/Spin.tsx b/src/components/Spin/Spin.tsx index 4b8e2b3a3b..7643ef9215 100644 --- a/src/components/Spin/Spin.tsx +++ b/src/components/Spin/Spin.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type {DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; diff --git a/src/components/Spin/__stories__/Spin.stories.tsx b/src/components/Spin/__stories__/Spin.stories.tsx index e24ce10fd5..80828c663d 100644 --- a/src/components/Spin/__stories__/Spin.stories.tsx +++ b/src/components/Spin/__stories__/Spin.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryFn} from '@storybook/react'; import {Spin} from '../Spin'; diff --git a/src/components/Switch/README-ru.md b/src/components/Switch/README-ru.md new file mode 100644 index 0000000000..dfaa0c33bc --- /dev/null +++ b/src/components/Switch/README-ru.md @@ -0,0 +1,153 @@ + + +# Switch + + + +```tsx +import {Switch} from '@gravity-ui/uikit'; +``` + +Компонент `Switch` (переключатель) используется для переключения между двумя состояниями: как правило, между **On** и **Off** или **Enabled** и **Disabled**. + +## Состояния + +`Switch` может иметь разные состояния: + +- Checked — когда переключатель **включен**. +- Disabled — когда переключатель недоступен. + + + + + +```tsx +Unchecked +Checked +Disabled +``` + + + +## Размер + +Размер `Switch` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx +M Size +L Size +``` + + + +## Лейбл + +Лейбл для `Switch` можно задать через свойство `content` или передать его как дочерний элемент. + + + + + +```tsx +
+ +
+ + Content as children + +
+
+``` + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------- | :--------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-------------------: | +| children | Содержимое переключателя (как правило, лейбл). | `ReactNode` | | +| content | Содержимое переключателя (альтернатива `children`). | `ReactNode` | | +| disabled | Включает или отключает состояние `disabled` у переключателя. | `boolean` | `false` | +| checked | Включает или отключает состояние `checked` у переключателя. | `boolean` | `false` | +| defaultChecked | Задает начальное состояние `checked` при монтировании компонента. | `boolean` | `false` | +| onUpdate | Срабатывает при изменении состояния переключателя пользователем и передает значение `checked` как аргумент обратного вызова. | `(checked: boolean) => void` | | +| onChange | Срабатывает при изменении состояния переключателя пользователем и передает событие изменения как аргумент обратного вызова. | `Function` | | +| onFocus | Обработчик события, вызываемый, когда элемент ввода переключателя получает фокус. | `Function` | | +| onBlur | Обработчик события, вызываемый, когда элемент ввода переключателя теряет фокус. | `Function` | | +| size | Определяет размер переключателя. | `m` `l` | `m` | +| id | HTML-атрибут `id`. | `string` | | +| qa | HTML-атрибут `data-qa`, используется для тестирования. | `string` | | +| style | HTML-атрибут `style`. | `React.CSSProperties` | | +| className | HTML-атрибут `class`. | `string` | | +| title | HTML-атрибут `title`. | `string` | | +| name | HTML-атрибут `name` для элемента ввода. | `string` | | +| value | HTML-атрибут `value` для элемента ввода. | `string` | | +| indeterminate | Включает или отключает состояние неопределенности переключателя. | `boolean` | `false` | +| controlProps | Дополнительные свойства базового элемента ввода. | `React.InputHTMLAttributes` | | +| controlRef | Ссылка на базовый элемент ввода. | `React.Ref` | | diff --git a/src/components/Switch/README.md b/src/components/Switch/README.md index f314db7c24..96ccd3658c 100644 --- a/src/components/Switch/README.md +++ b/src/components/Switch/README.md @@ -8,14 +8,14 @@ import {Switch} from '@gravity-ui/uikit'; ``` -The Switch component is used to toggle between two states, typically representing "on" and "off" or "enabled" and "disabled" states. +The `Switch` component is used to toggle between two states: typically, between **on** and **off**, or **enabled** and **disabled**. ## States -The Switch can be in different states. +A `Switch` can have different states: -- Checked - when the switch is in the "On" state. -- Disabled - when the switch is unavailable for interaction. +- Checked: When the switch has the **On** state. +- Disabled: When the switch is unavailable. ## Size -To control the size of the `Switch`, use the `size` property. The default size is `m`. +Use the `size` property to manage the `Switch` size. The default size is `m`. ## Label -You can set a label for a `Switch` component using the `content` property or pass it as children. +You can assign a label to a `Switch` using the `content` property or provide it as a child property. | Name | Description | Type | Default | | :------------- | :------------------------------------------------------------------------------------------------------- | :-------------------------------------------: | :-----: | -| children | The content of the switch (usually a label). | `ReactNode` | | -| content | The content of the switch (alternative to children). | `ReactNode` | | -| disabled | Toggles the `disabled` state of the switch. | `boolean` | `false` | -| checked | Toggles the checked state of the switch. | `boolean` | `false` | -| defaultChecked | Sets the initial checked state when the component is mounted. | `boolean` | `false` | -| onUpdate | Fires when the switch state is changed by the user. Provides the checked value as a callback's argument. | `(checked: boolean) => void` | | -| onChange | Fires when the switch state is changed by the user. Provides the change event as a callback's argument. | `Function` | | -| onFocus | Event handler for when the switch input element receives focus. | `Function` | | -| onBlur | Event handler for when the switch input element loses focus. | `Function` | | -| size | Sets the size of the switch. | `m` `l` | `m` | -| id | HTML `id` attribute | `string` | | -| qa | HTML `data-qa` attribute, used in tests. | `string` | | -| style | HTML `style` attribute | `React.CSSProperties` | | -| className | HTML `class` attribute | `string` | | -| title | HTML `title` attribute | `string` | | -| name | HTML `name` attribute for the input element. | `string` | | -| value | HTML `value` attribute for the input element. | `string` | | -| indeterminate | Toggles the indeterminate state of the switch. | `boolean` | `false` | -| controlProps | Additional props for the underlying input element. | `React.InputHTMLAttributes` | | -| controlRef | Ref to the underlying input element. | `React.Ref` | | +| children | The content of the switch (usually, a label) | `ReactNode` | | +| content | The content of the switch (alternative to children) | `ReactNode` | | +| disabled | Toggles the `disabled` state of the switch | `boolean` | `false` | +| checked | Toggles the `checked` state of the switch | `boolean` | `false` | +| defaultChecked | Sets the initial checked state when the component is mounted | `boolean` | `false` | +| onUpdate | Fires when the switch state is changed by the user and provides the checked value as a callback argument | `(checked: boolean) => void` | | +| onChange | Fires when the switch state is changed by the user and provides the change event as a callback argument | `Function` | | +| onFocus | Event handler to use when the switch input element receives focus | `Function` | | +| onBlur | Event handler to use when the switch input element loses focus | `Function` | | +| size | Sets the size of the switch | `m` `l` | `m` | +| id | `id` HTML attribute | `string` | | +| qa | `data-qa` HTML attribute, used for testing | `string` | | +| style | `style` HTML attribute | `React.CSSProperties` | | +| className | `class` HTML attribute | `string` | | +| title | `title` HTML attribute | `string` | | +| name | `name` HTML attribute for the input element | `string` | | +| value | `value` HTML attribute for the input element | `string` | | +| indeterminate | Toggles the indeterminate state of the switch | `boolean` | `false` | +| controlProps | Additional propeties for the underlying input element | `React.InputHTMLAttributes` | | +| controlRef | Ref to the underlying input element | `React.Ref` | | diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 020070d873..bdb284f1cd 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {useCheckbox} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; diff --git a/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-dark-chromium-linux.png b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-dark-chromium-linux.png new file mode 100644 index 0000000000..c5b627e20c Binary files /dev/null and b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-dark-chromium-linux.png differ diff --git a/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-light-chromium-linux.png b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-light-chromium-linux.png new file mode 100644 index 0000000000..891fbbf17e Binary files /dev/null and b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-checked-light-chromium-linux.png differ diff --git a/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-dark-chromium-linux.png b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-dark-chromium-linux.png new file mode 100644 index 0000000000..24c038be04 Binary files /dev/null and b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-dark-chromium-linux.png differ diff --git a/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-light-chromium-linux.png b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..7cb54727dd Binary files /dev/null and b/src/components/Switch/__snapshots__/Switch.visual.test.tsx-snapshots/Switch-smoke-light-chromium-linux.png differ diff --git a/src/components/Switch/__stories__/Switch.stories.tsx b/src/components/Switch/__stories__/Switch.stories.tsx index 021cc45f3a..af82f49c7e 100644 --- a/src/components/Switch/__stories__/Switch.stories.tsx +++ b/src/components/Switch/__stories__/Switch.stories.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {Meta, StoryObj} from '@storybook/react'; import {Showcase} from '../../../demo/Showcase'; diff --git a/src/components/Switch/__stories__/SwitchShowcase.tsx b/src/components/Switch/__stories__/SwitchShowcase.tsx index e83e3bfc2c..9bf5a2e9ca 100644 --- a/src/components/Switch/__stories__/SwitchShowcase.tsx +++ b/src/components/Switch/__stories__/SwitchShowcase.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {Showcase} from '../../../demo/Showcase'; import {ShowcaseItem} from '../../../demo/ShowcaseItem'; import {Switch} from '../Switch'; diff --git a/src/components/Switch/__tests__/Switch.test.tsx b/src/components/Switch/__tests__/Switch.test.tsx index e798c1ab42..d7645a0c9a 100644 --- a/src/components/Switch/__tests__/Switch.test.tsx +++ b/src/components/Switch/__tests__/Switch.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Switch/__tests__/Switch.visual.test.tsx b/src/components/Switch/__tests__/Switch.visual.test.tsx new file mode 100644 index 0000000000..36ef666a8c --- /dev/null +++ b/src/components/Switch/__tests__/Switch.visual.test.tsx @@ -0,0 +1,65 @@ +import {smokeTest, test} from '~playwright/core'; + +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import type {SwitchProps} from '../Switch'; +import {Switch} from '../Switch'; + +import {disabledCases, indeterminateCases, sizeCases} from './cases'; + +test.describe('Switch', {tag: '@Switch'}, () => { + const defaultProps: SwitchProps = { + content: 'label', + }; + + smokeTest('', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios(defaultProps, { + size: sizeCases, + disabled: disabledCases, + indeterminate: indeterminateCases, + }); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); + + smokeTest('checked', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + { + ...defaultProps, + checked: true, + }, + { + size: sizeCases, + disabled: disabledCases, + indeterminate: indeterminateCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({}); + }); +}); diff --git a/src/components/Switch/__tests__/cases.tsx b/src/components/Switch/__tests__/cases.tsx new file mode 100644 index 0000000000..addb48111c --- /dev/null +++ b/src/components/Switch/__tests__/cases.tsx @@ -0,0 +1,6 @@ +import type {Cases} from '../../../stories/tests-factory/models'; +import type {SwitchProps} from '../Switch'; + +export const disabledCases: Cases = [true]; +export const indeterminateCases: Cases = [true]; +export const sizeCases: Cases = ['m', 'l']; diff --git a/src/components/Table/README-ru.md b/src/components/Table/README-ru.md new file mode 100644 index 0000000000..0b730fa129 --- /dev/null +++ b/src/components/Table/README-ru.md @@ -0,0 +1,380 @@ + + +## Table (таблица) + + + +```jsx +import {Table} from '@gravity-ui/uikit'; +``` + +Компонент `Table` позволяет выбирать и сортировать строки, а также выполнять действия с выбранной строкой. + + + +Дополнительные функции подключаются через компоненты высшего порядка (HOC): + +- [withTableActions](#usage-with-hoc-withtableactions) +- [withTableCopy](#usage-with-hoc-withtablecopy) +- [withTableSelection](#usage-with-hoc-withtableselection) +- [withTableSettings](#usage-with-hoc-withtablesettings) +- [withTableSorting](#usage-with-hoc-withtablesorting) + + + +## Свойства + +| Имя | Описание | Тип | Значение по умолчанию | +| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------: | :-------------------: | +| data | Данные. | `any[]` | | +| columns | Настройки столбцов. | `TableColumnConfig[]` | | +| verticalAlign | Выравнивание содержимого по вертикали. | `"top"` `"middle"` | | +| getRowDescriptor | Обработчик для получения дескриптора строки. | `(item: any, index: number) => DescriptorType` | | +| getRowId | Идентификатор строки, используемый при выборе и сортировке строк. Если пропустить строку, то идентификатор такой строки будет равен значению поля в данных строки с тем же именем, что и идентификатор столбца. | `string` `((item: any, index: number) => string)` | | +| getRowClassNames | CSS-классы строки. | `(item: any, index: number) => string[]` | | +| isRowDisabled | Условие блокировки столбцов. | `(item: any, index: number) => boolean` | | +| onRowClick | Обработчик клика (`click`) по строке. | `(item: any, index: number, event: React.MouseEvent) => void` | | +| onRowMouseEnter | Обработчик наведения мыши (`mouseenter`) на строку. | `(item: any, index: number, event: React.MouseEvent) => void` | | +| onRowMouseLeave | Обработчик ухода мыши (`mouseleave`) со строки. | `(item: any, index: number, event: React.MouseEvent) => void` | | +| emptyMessage | Возвращает сообщение, если данные отсутствуют. | `string` | `"No data"` | +| className | CSS-класс таблицы. | `string` | | +| edgePadding | Добавляет горизонтальные отступы для крайних ячеек. | `boolean` | | +| stickyHorizontalScroll | Добавляет горизонтальную липкую прокрутку (sticky scroll) в таблице. Обратите внимание, что таблица не может иметь фиксированную высоту и липкую прокрутку одновременно. Липкая прокрутка не будет работать при переполнении таблицы. | `boolean` | `false` | +| stickyHorizontalScrollBreakpoint | Порог, которого должен достичь родительский блок, чтобы прокрутка стала липкой. Это особенно удобно в консоли, когда панель `groupActions` перекрывает область прокрутки. | `number` | `0` | + +### DescriptorType + +| Имя | Описание | Тип | Значение по умолчанию | +| :--------- | :---------------------------------------------------------------- | :---------: | :-------------------: | +| id | Идентификатор строки, используемый при выборе и сортировке строк. | `string` | | +| disabled | Условие блокировки столбцов. | `boolean` | | +| classNames | CSS-классы строки. | ` string[]` | | + +### TableColumnConfig + +| Имя | Описание | Тип | Значение по умолчанию | +| :---------- | :---------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------: | :---------------------------------------------------------------: | +| id | Идентификатор столбца. | `string` | | +| name | Название (заголовок) столбца. | `string` `(() => React.ReactNode)` | Идентификатор столбца. | +| className | CSS-класс, который будет добавлен ко всем ячейкам в столбце. | `string` | | +| placeholder | Заглушка при отсутствии данных в ячейке. | `string` `((item: any, index: number) => React.ReactNode)` | `— (—)` | +| template | Содержимое ячейки. Если пропустить строку, ячейка будет содержать значение поля с таким же именем, как и у этой строки. | `string` `((item: any, index: number) => React.ReactNode)` | Значение поля, имя которого соответствует идентификатору столбца. | +| align | Выравнивание содержимого. | `"start"` `"center"` `"end"` | | +| sticky | Липкий столбец. | `"start"` `"end"` | | +| primary | Указывает, что столбец является первичным относительно остальных. | `boolean` | | +| width | Ширина содержимого столбца в пикселях. | `number` `string` | | +| meta | Различные данные, включая настройки HOC. | `Record` | | + +## Использование `Table` с HOC `withTableActions` + +Этот HOC добавляет к столбцам таблицы специальный столбец с действиями. + +### Свойства + +| Имя | Описание | Тип | +| :--------------- | :---------------------------------------------------- | :------------------------------------------------------: | +| getRowActions | Массив конфигураций действий для каждой строки. | `(item: any, index: number) => TableActionConfig[]` | +| renderRowActions | Функция рендеринга ячейки с действиями. | `(props: {item: any; index: number}) => React.ReactNode` | +| rowActionsSize | Размер кнопки действия и элементов всплывающего меню. | `"s"` `"m"` `"l"` `"xl"` | + +### TableActionConfig + +```ts +type TableActionConfig = TableAction | TableActionGroup; +``` + +#### TableAction + +| Имя | Описание | Тип | Значение по умолчанию | +| :------- | :---------------------------------------------------------------------------- | :----------------------------------: | :-------------------: | +| text | Текст. | `string` | | +| handler | Обработчик клика. | `(item: any, index: number) => void` | | +| disabled | Действие отключено. | `boolean` | | +| href | Элемент меню с этим свойством становится ссылкой на указанное местоположение. | `string` | | +| target | То же, что и атрибут `target` у тега ``. | `string` | | +| rel | То же, что и атрибут `rel` у тега ``. | `string` | | +| theme | Тема. | `"normal"` `"danger"` | `"normal"` | +| icon | Иконка, отображаемая рядом с текстом. | `React.ReactNode` | | + +#### TableActionGroup + +| Имя | Описание | Тип | +| :---- | :------------------------- | :-------------------: | +| title | Заголовок группы действий. | `string` | +| items | Элементы группы действий. | `TableActionConfig[]` | + +### Пример + +```jsx +import {Table, withTableActions} from '@gravity-ui/uikit'; + +const MyTable = withTableActions(Table); +const data = [ + {id: 1, text: 'Hello'}, + {id: 2, text: 'World'}, +]; +const columns = [{id: 'id'}, {id: 'text'}]; +const getRowActions = () => { + return [ + { + text: 'Print', + handler: () => {}, + }, + { + text: 'Remove', + handler: () => {}, + theme: 'danger', + }, + ]; +}; + +const table = ; +``` + +```jsx +import {Table, withTableActions, RenderRowActionsProps} from '@gravity-ui/uikit'; + +const MyTable = withTableActions(Table); +type Item = {id: number; text: string}; + +const data: Item[] = [ + {id: 1, text: 'Hello'}, + {id: 2, text: 'World'}, +]; +const columns = [{id: 'id'}, {id: 'text'}]; + +const RowAction = ({item}: RenderRowActionsProps) => { + return {`Action for - ${item.text}`}; +}; + +const table = ( + +); +``` + +## Использование `Table` с HOC `withTableCopy` + +Этот HOC позволяет копировать содержимое ячейки или произвольный текст. + +### ColumnMeta + +| Имя | Описание | Тип | +| :--- | :-------------------------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------: | +| copy | Копируемый текст. Если установлено значение `true`, содержимое ячейки можно копировать. | `boolean` `((item: any, index: number) => string)` `((item: any, index: number) => number)` | + +### Пример + +```jsx +import {Table, withTableCopy} from '@gravity-ui/uikit'; + +const MyTable = withTableCopy(Table); +const data = [ + {id: 1, text: 'Hello'}, + {id: 2, text: 'World'}, +]; +const columns = [ + {id: 'id', meta: {copy: ({id}) => `ID #${id}`}}, + {id: 'text', meta: {copy: true}}, +]; + +const table = ; +``` + +## Использование `Table` с HOC `withTableSelection` + +Этот HOC позволяет выбирать строки в таблице. + +### Свойства + +| Имя | Описание | Тип | +| :---------------- | :------------------------------------ | :-----------------------: | +| selectedIds | Выбранные строки. | `string[]` | +| onSelectionChange | Обработчик изменения выбранных строк. | `(ids: string[]) => void` | + +### Пример + +```jsx +import {Table, withTableSelection} from '@gravity-ui/uikit'; + +const MyTable = withTableSelection(Table); +const data = [ + {id: 1, text: 'Hello'}, + {id: 2, text: 'World'}, +]; +const columns = [{id: 'id'}, {id: 'text'}]; +const getRowId = 'id'; + +function SelectionTable() { + const [selectedIds, setSelectedIds] = React.useState([1]); + + return ( + + ); +} +``` + +## Использование `Table` с HOC `withTableSettings` + +Этот HOC активирует функции для настройки столбцов таблицы. Его можно использовать двумя способами: + +```jsx +import {Table, withTableSettings} from './withTableSettings'; + +// No options passed +const MyTable1 = withTableSettings(Table); +// or with options +const MyTable1 = withTableSettings({sortable: false})(Table); +``` + +### Опции + +| Имя | Описание | Тип | Значение по умолчанию | +| :--------- | :---------------------------------------------------- | :--------------: | :-------------------: | +| width | Ширина всплывающего окна с настройками. | `number` `"fit"` | | +| sortable | Включает или отключает сортировку элементов настроек. | `boolean` | `true` | +| filterable | Включает или отключает фильтрацию элементов настроек. | `boolean` | `false` | + +### ColumnMeta + +| Имя | Описание | Тип | Значение по умолчанию | +| :---------------- | :-------------------------------------------------------------------------------------------------------- | :-------: | :-------------------: | +| selectedByDefault | Включает или отключает автоматический выбор столбца, если он не передан в настройках. | `boolean` | `true` | +| selectedAlways | При включении этого свойства столбец всегда остается выбранным. Изменить видимость такого столбца нельзя. | `boolean` | `false` | + +### Свойства + +| Имя | Описание | Тип | +| :------------------------- | :--------------------------------------------------------------------------- | :------------------------------------------------------: | +| settingsPopupWidth | Ширина всплывающего окна `TableColumnSetup`. | `number` `"fit"` | +| settings | Текущие настройки. | `TableSettingsData` | +| updateSettings | Обработчик обновления настроек. | `(data: TableSettingsData) => Promise` | +| renderControls | Позволяет рендерить пользовательские действия. | `RenderControls` | +| settingsFilterPlaceholder | Текст, который отображается в контроле, когда значение для поиска не задано. | `string` | +| settingsFilterEmptyMessage | Текст, который отображается, когда ни один элемент не найден. | `string` | +| filterSettings | Функция для фильтрации элементов. | `(value: string, item: TableColumnSetupItem) => boolean` | + +### TableSettingsData + +```ts +type TableSettingsData = Array<{ + id: string; + isSelected?: boolean; +}>; +``` + +### RenderControls + +```ts +type RenderControls = (params: { + DefaultApplyButton: React.ComponentType; + onApply: () => void; +}) => React.ReactNode; +``` + +### Пример + +```jsx +import {Table, withTableSettings} from '@gravity-ui/uikit'; + +const MyTable = withTableSettings({width: 100, sortable: false})(Table); +const data = [ + {id: 1, text: 'Hello'}, + {id: 2, text: 'World'}, +]; +const columns = [{id: 'id'}, {id: 'text'}]; +const initialSettings = [ + {id: 'id', isSelected: false}, + {id: 'text', isSelected: true}, +]; + +function SelectionTable() { + const [settings, setSettings] = React.useState(initialSettings); + + return ( + { + setSettings(settings); + return Promise.resolve(); + }} + renderControls={({DefaultApplyButton, onApply}) => ( + + + + + )} + /> + ); +} +``` + +## Использование `Table` с HOC `withTableSorting` + +Этот HOC позволяет выполнить сортировку столбцов. + +### ColumnMeta + +| Имя | Описание | Тип | Значение по умолчанию | +| :--------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------: | :-------------------: | +| defaultSortOrder | Устанавливает первичный порядок сортировки. | `"asc"` `"desc"` | `asc` | +| sort | Функция сортировки. Возвращает значение для сортировки по возрастанию. При установке `true` значения ячеек сравниваются и сортируются в порядке возрастания. | `boolean` `((itemA: any, itemB: any) => number)` | | + +### Свойства + +| Имя | Описание | Тип | +| :---------------- | :------------------------------------------------------------------ | :-----------------------------------: | +| defaultSortState | Состояние сортировки по умолчанию для неконтролируемого компонента. | `TableSortState` | +| sortState | Состояние сортировки. | `TableSortState` | +| onSortStateChange | Обработчик изменения состояния сортировки. | `(sortState: TableSortState) => void` | + +Если не передавать свойства `sortState` и `onSortStateChange`, то состояние сортировки будет храниться в самом компоненте. + +### TableSortState + +```ts +type TableSortState = Array<{ + column: string; + order: 'asc' | 'desc'; +}>; +``` + +### Пример + +```jsx +import {Table, withTableSorting} from '@gravity-ui/uikit'; + +const MyTable = withTableSorting(Table); +const data = [ + {id: 1, text: 'Hello', date: '2016-10-25'}, + {id: 2, text: 'World', date: '2020-08-15'}, +]; +const columns = [ + {id: 'id', meta: {sort: true}}, + { + id: 'text', + meta: {defaultSortOrder: 'desc', sort: (a, b) => Date.parse(a.date) - Date.parse(b.date)}, + }, +]; + +const table = ; +``` diff --git a/src/components/Table/README.md b/src/components/Table/README.md index 4e509ca448..1c9e276f09 100644 --- a/src/components/Table/README.md +++ b/src/components/Table/README.md @@ -8,11 +8,11 @@ import {Table} from '@gravity-ui/uikit'; ``` -A table that allows the selecting and sorting of rows, and performing actions on a row. +A `Table` allows selecting and sorting rows, as well as performing actions on a row. -Additional functionality is enabled via HOCs: +Additional features are enabled through HOCs: - [withTableActions](#usage-with-hoc-withtableactions) - [withTableCopy](#usage-with-hoc-withtablecopy) @@ -24,59 +24,58 @@ Additional functionality is enabled via HOCs: ## Properties -| Name | Description | Type | Default | -| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------: | :---------: | -| data | Data | `any[]` | | -| columns | Column parameters | `TableColumnConfig[]` | | -| verticalAlign | Vertical alignment of contents | `"top"` `"middle"` | | -| getRowDescriptor | Handler to get row descriptor | `(item: any, index: number) => DescriptorType` | | -| getRowId | The row ID, used when selecting and sorting rows. If you skip a row, its ID will be the value of the field in the row data with the same name as the column ID | `string` `((item: any, index: number) => string)` | | -| getRowClassNames | Row CSS classes | `(item: any, index: number) => string[]` | | -| isRowDisabled | Condition for disabling columns | `(item: any, index: number) => boolean` | | -| onRowClick | Row click handler | `(item: any, index: number, event: React.MouseEvent) => void` | | -| onRowMouseEnter | Row mouseenter handler | `(item: any, index: number, event: React.MouseEvent) => void` | | -| onRowMouseLeave | Row mouseleave handler | `(item: any, index: number, event: React.MouseEvent) => void` | | -| emptyMessage | The message returned if data is missing. | `string` | `"No data"` | -| className | Table CSS class | `string` | | -| edgePadding | Adds horizontal padding for edge cells | `boolean` | | -| stickyHorizontalScroll | A horizontal sticky scroll in a table. NB: A table cannot have a fixed height and a sticky scroll at the same time. A sticky scroll will not work if the table has an overflow. | `boolean` | `false` | -| stickyHorizontalScrollBreakpoint | The threshold that the parent block should reach before making a scroll sticky. This is useful in the console, for example, when the groupActions bar closes the scroll. | `number` | `0` | -| `width` | Table width | `"auto"` `"max"` | "auto" | +| Name | Description | Type | Default | +| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------: | :---------: | +| data | Data | `any[]` | | +| columns | Column settings | `TableColumnConfig[]` | | +| verticalAlign | Vertical alignment of content | `"top"` `"middle"` | | +| getRowDescriptor | Handler to get the row descriptor | `(item: any, index: number) => DescriptorType` | | +| getRowId | Row ID used when selecting and sorting rows. If you skip a row, its ID will be the value of the field in the row data with the same name as the column ID. | `string` `((item: any, index: number) => string)` | | +| getRowClassNames | Row CSS classes | `(item: any, index: number) => string[]` | | +| isRowDisabled | Condition for disabling columns | `(item: any, index: number) => boolean` | | +| onRowClick | Row click handler | `(item: any, index: number, event: React.MouseEvent) => void` | | +| onRowMouseEnter | Row mouseenter handler | `(item: any, index: number, event: React.MouseEvent) => void` | | +| onRowMouseLeave | Row mouseleave handler | `(item: any, index: number, event: React.MouseEvent) => void` | | +| emptyMessage | Returning a message if the data is missing | `string` | `"No data"` | +| className | Table CSS class | `string` | | +| edgePadding | Adds horizontal padding for edge cells | `boolean` | | +| stickyHorizontalScroll | Adds a horizontal sticky scroll in a table. Note: A table cannot have a fixed height and a sticky scroll at the same time. A sticky scroll will not work if the table has an overflow. | `boolean` | `false` | +| stickyHorizontalScrollBreakpoint | Threshold the parent block should reach before making a scroll sticky. This is useful in the console, such as when the `groupActions` bar overlaps the scroll. | `number` | `0` | ### DescriptorType -| Name | Description | Type | Default | -| :--------- | :----------------------------------------------- | :---------: | :-----: | -| id | The row ID, used when selecting and sorting rows | `string` | | -| disabled | Condition for disabling columns | `boolean` | | -| classNames | Row CSS classes | ` string[]` | | +| Name | Description | Type | Default | +| :--------- | :------------------------------------------ | :---------: | :-----: | +| id | Row ID used when selecting and sorting rows | `string` | | +| disabled | Condition for disabling columns | `boolean` | | +| classNames | Row CSS classes | ` string[]` | | ### TableColumnConfig -| Name | Description | Type | Default | -| :---------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------: | :---------------------------------------------------------: | -| id | Column ID | `string` | | -| name | Column name (header) | `string` `(() => React.ReactNode)` | column ID | -| className | CSS-class that will be added to all cells in the column | `string` | | -| placeholder | The stub when there is no data in a cell | `string` `((item: any, index: number) => React.ReactNode)` | `— (—)` | -| template | Cell contents. If you skip a row, the cell contents will be the value of the field with the same name as this row. | `string` `((item: any, index: number) => React.ReactNode)` | The value of the field with the name equal to the column ID | -| align | Content alignment | `"start"` `"center"` `"end"` | | -| sticky | Sticky column | `"start"` `"end"` | | -| primary | Distinguishes a column among other | `boolean` | | -| width | Column's content width in px | `number` `string` | | -| meta | Various data, HOC settings | `Record` | | +| Name | Description | Type | Default | +| :---------- | :----------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------: | :-----------------------------------------------------: | +| id | Column ID | `string` | | +| name | Column name (header) | `string` `(() => React.ReactNode)` | column ID | +| className | CSS class that will be added to all cells in the column | `string` | | +| placeholder | Stub when there is no data in a cell | `string` `((item: any, index: number) => React.ReactNode)` | `— (—)` | +| template | Cell contents. If you skip a row, the cell contents will be the value of the field with the same name as this row. | `string` `((item: any, index: number) => React.ReactNode)` | Value of the field with the name equal to the column ID | +| align | Content alignment | `"start"` `"center"` `"end"` | | +| sticky | Sticky column | `"start"` `"end"` | | +| primary | Identifies a column as primary as opposed to others | `boolean` | | +| width | Column's content width in pixels | `number` `string` | | +| meta | Miscellaneous data including the HOC settings | `Record` | | -## Usage with HOC `withTableActions` +## Using `Table` with the `withTableActions` HOC -Adds a special column with actions to table columns. +This HOC adds a special column with actions to table columns. ### Properties -| Name | Description | Type | -| :--------------- | :------------------------------------------ | :------------------------------------------------------: | -| getRowActions | Array of action configs for each row | `(item: any, index: number) => TableActionConfig[]` | -| renderRowActions | render function for Actions Cell | `(props: {item: any; index: number}) => React.ReactNode` | -| rowActionsSize | Size of actions button and popup menu items | `"s"` `"m"` `"l"` `"xl"` | +| Name | Description | Type | +| :--------------- | :--------------------------------------------- | :------------------------------------------------------: | +| getRowActions | Array of action configs for each row | `(item: any, index: number) => TableActionConfig[]` | +| renderRowActions | Render function for Actions Cell | `(props: {item: any; index: number}) => React.ReactNode` | +| rowActionsSize | Size of the action button and popup menu items | `"s"` `"m"` `"l"` `"xl"` | ### TableActionConfig @@ -86,16 +85,16 @@ type TableActionConfig = TableAction | TableActionGroup; #### TableAction -| Name | Description | Type | Default | -| :------- | :----------------------------------------------------------------- | :----------------------------------: | :--------: | -| text | Text | `string` | | -| handler | Click handler | `(item: any, index: number) => void` | | -| disabled | Action disabled | `boolean` | | -| href | Menu item with this prop becomes a link to the specified location. | `string` | | -| target | Same as the `target` attribute of the `` tag. | `string` | | -| rel | Same as the `rel` attribute of the `` tag. | `string` | | -| theme | Theme | `"normal"` `"danger"` | `"normal"` | -| icon | Icon to display next to the text | `React.ReactNode` | | +| Name | Description | Type | Default | +| :------- | :----------------------------------------------------------------------- | :----------------------------------: | :--------: | +| text | Text | `string` | | +| handler | Click handler | `(item: any, index: number) => void` | | +| disabled | Action disabled | `boolean` | | +| href | A menu item with this property becomes a link to the specified location. | `string` | | +| target | Same as the `target` attribute of the `` tag. | `string` | | +| rel | Same as the `rel` attribute of the `` tag. | `string` | | +| theme | Theme | `"normal"` `"danger"` | `"normal"` | +| icon | Icon to display next to the text | `React.ReactNode` | | #### TableActionGroup @@ -157,15 +156,15 @@ const table = ( ); ``` -## Usage with HOC `withTableCopy` +## Using `Table` with the `withTableCopy` HOC -Allows the contents of a cell or any text to be copied. +This HOC enables copying the contents of a cell or any other text. ### ColumnMeta -| Name | Description | Type | -| :--- | :---------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------: | -| copy | The text to be copies. If true is passed, the cell contents are copied. | `boolean` `((item: any, index: number) => string)` `((item: any, index: number) => number)` | +| Name | Description | Type | +| :--- | :-------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------: | +| copy | Text to copy. If the value is true, copying cell contents is allowed. | `boolean` `((item: any, index: number) => string)` `((item: any, index: number) => number)` | ### Example @@ -185,15 +184,15 @@ const columns = [ const table = ; ``` -## Usage with HOC `withTableSelection` +## Using `Table` with the `withTableSelection` HOC -Enables the selection of table rows. +This HOC enables selecting table rows. ### Properties | Name | Description | Type | | :---------------- | :-------------------------- | :-----------------------: | -| selectedIds | Rows selected | `string[]` | +| selectedIds | Selected rows | `string[]` | | onSelectionChange | Selected row change handler | `(ids: string[]) => void` | ### Example @@ -224,9 +223,9 @@ function SelectionTable() { } ``` -## Usage with HOC `withTableSettings` +## Using `Table` with the `withTableSettings` HOC -Enables functionality for table column settings. You can use ut in two forms: +This HOC enables features for table column settings. You can use it in two ways: ```jsx import {Table, withTableSettings} from './withTableSettings'; @@ -239,32 +238,30 @@ const MyTable1 = withTableSettings({sortable: false})(Table); ### Options -| Name | Description | Type | Default | -| :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | :-----------------: | :-----: | -| width | Settings' popup width | `number` `"fit"` | | -| sortable | Whether or not add ability to sort settings items | `boolean` | `true` | -| filterable | Whether or not add ability to filter settings items | `boolean` | `false` | -| defaultSettings | Settings to which you can reset the current settings | `TableSettingsData` | | -| showResetButton | Display a reset button that resets the current settings changes. If the `defaultSettings` prop is set then the settings reset to the `defaultSettings`. | `boolean` | | +| Name | Description | Type | Default | +| :--------- | :------------------------------------------- | :--------------: | :-----: | +| width | Settings popup width | `number` `"fit"` | | +| sortable | Enables or disables sorting settings items | `boolean` | `true` | +| filterable | Enables or disables filtering settings items | `boolean` | `false` | ### ColumnMeta -| Name | Description | Type | Default | -| :---------------- | :------------------------------------------------------------------------- | :-------: | :-----: | -| selectedByDefault | Specifies whether a column is selected if it is missing from the settings. | `boolean` | `true` | -| selectedAlways | Makes the column always selected. You cannot change its visibility. | `boolean` | `false` | +| Name | Description | Type | Default | +| :---------------- | :------------------------------------------------------------------------ | :-------: | :-----: | +| selectedByDefault | Enables or disables selecting a column if it is missing from the settings | `boolean` | `true` | +| selectedAlways | Makes the column always selected. You cannot change its visibility. | `boolean` | `false` | ### Properties -| Name | Description | Type | -| :------------------------- | :------------------------------------------------------------------------- | :------------------------------------------------------: | -| settingsPopupWidth | TableColumnSetup pop-up width | `number` `"fit"` | -| settings | Current settings | `TableSettingsData` | -| updateSettings | Settings update handle | `(data: TableSettingsData) => Promise` | -| renderControls | (deprecated) use `defaultSettings` and `showResetButton` to reset settings | `RenderControls` | -| settingsFilterPlaceholder | Text that appears in the control when no search value is set | `string` | -| settingsFilterEmptyMessage | Text that appears when no one item is found | `string` | -| filterSettings | Function for filtering items | `(value: string, item: TableColumnSetupItem) => boolean` | +| Name | Description | Type | +| :------------------------- | :----------------------------------------------------------- | :------------------------------------------------------: | +| settingsPopupWidth | `TableColumnSetup` pop-up width | `number` `"fit"` | +| settings | Current settings | `TableSettingsData` | +| updateSettings | Settings update handler | `(data: TableSettingsData) => Promise` | +| renderControls | Enables rendering custom actions | `RenderControls` | +| settingsFilterPlaceholder | Text that appears in the control when no search value is set | `string` | +| settingsFilterEmptyMessage | Text that appears when no item is found | `string` | +| filterSettings | Function for filtering items | `(value: string, item: TableColumnSetupItem) => boolean` | ### TableSettingsData @@ -331,16 +328,16 @@ function SelectionTable() { } ``` -## Usage with HOC `withTableSorting` +## Using `Table` with the `withTableSorting` HOC -Enables column sorting. +This HOC enables column sorting. ### ColumnMeta -| Name | Description | Type | Default | -| :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------: | :-----: | -| defaultSortOrder | Sets the primary sorting order | `"asc"` `"desc"` | `asc` | -| sort | The sorting function. It should return a value for sorting in ascending order. If true is passed, cell values are compared and sorted in ascending order. | `boolean` `((itemA: any, itemB: any) => number)` | | +| Name | Description | Type | Default | +| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------: | :-----: | +| defaultSortOrder | Sets the primary sorting order | `"asc"` `"desc"` | `asc` | +| sort | Sorting function. It should return a value for sorting in the ascending order. If set to true, the cell values are compared and sorted in the ascending order. | `boolean` `((itemA: any, itemB: any) => number)` | | ### Properties @@ -350,7 +347,7 @@ Enables column sorting. | sortState | Sorting state | `TableSortState` | | onSortStateChange | Sorting state change handle | `(sortState: TableSortState) => void` | -If the `sortState` and `onSortStateChange` props are not passed, the sorting state is stored in the component itself. +If the `sortState` and `onSortStateChange` properties are missing, the sorting state is stored in the component itself. ### TableSortState diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index acfdbe6d72..121a95bcc0 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import _get from 'lodash/get'; import _has from 'lodash/has'; diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Adaptive-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Adaptive-light-chromium-linux.png new file mode 100644 index 0000000000..e05477eb8c Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Adaptive-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-chromium-linux.png deleted file mode 100644 index d360ec3944..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-chromium-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-webkit-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-webkit-linux.png deleted file mode 100644 index a9cd718ed0..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-chromium-linux.png index 2cce9f0e31..7c30526e0c 100644 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-chromium-linux.png and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-webkit-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-webkit-linux.png deleted file mode 100644 index 1c12d8c11f..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-Default-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyCustom-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyCustom-light-chromium-linux.png new file mode 100644 index 0000000000..8c2d125800 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyCustom-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyDefault-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyDefault-light-chromium-linux.png new file mode 100644 index 0000000000..437056edb0 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-EmptyDefault-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-chromium-linux.png deleted file mode 100644 index df5744b1e4..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-chromium-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-webkit-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-webkit-linux.png deleted file mode 100644 index 609f9ee9b4..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-dark-webkit-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-chromium-linux.png index 31292b1dde..f323ebbaf8 100644 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-chromium-linux.png and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-webkit-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-webkit-linux.png deleted file mode 100644 index 14a346142d..0000000000 Binary files a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-render-story-HOCWithTableSorting-light-webkit-linux.png and /dev/null differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-column-config-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-column-config-light-chromium-linux.png new file mode 100644 index 0000000000..256184a1f7 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-column-config-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-light-chromium-linux.png new file mode 100644 index 0000000000..c0b4ab1739 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-actions-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-actions-light-chromium-linux.png new file mode 100644 index 0000000000..d591b564aa Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-actions-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-checkbox-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-checkbox-light-chromium-linux.png new file mode 100644 index 0000000000..2f14669a46 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-checkbox-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-copy-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-copy-light-chromium-linux.png new file mode 100644 index 0000000000..c91c6bca0d Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-copy-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-filterable-settings-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-filterable-settings-light-chromium-linux.png new file mode 100644 index 0000000000..ab98d02b18 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-filterable-settings-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-settings-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-settings-light-chromium-linux.png new file mode 100644 index 0000000000..3accf5a598 Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-settings-light-chromium-linux.png differ diff --git a/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-table-settings-light-chromium-linux.png b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-table-settings-light-chromium-linux.png new file mode 100644 index 0000000000..b9a30b9c9c Binary files /dev/null and b/src/components/Table/__snapshots__/Table.visual.test.tsx-snapshots/Table-smoke-with-table-settings-light-chromium-linux.png differ diff --git a/src/components/Table/__stories__/Adaptive.tsx b/src/components/Table/__stories__/Adaptive.tsx index 4a1fa44be9..2a2a12691f 100644 --- a/src/components/Table/__stories__/Adaptive.tsx +++ b/src/components/Table/__stories__/Adaptive.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import type {StoryFn} from '@storybook/react'; import _cloneDeep from 'lodash/cloneDeep'; diff --git a/src/components/Table/__stories__/Table.stories.tsx b/src/components/Table/__stories__/Table.stories.tsx index ca6e588c39..0108d6534d 100644 --- a/src/components/Table/__stories__/Table.stories.tsx +++ b/src/components/Table/__stories__/Table.stories.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {Pencil} from '@gravity-ui/icons'; import {action} from '@storybook/addon-actions'; @@ -201,7 +201,7 @@ const WithTableSelectionTemplate: StoryFn> = (args) => { }; export const HOCWithTableSelection = WithTableSelectionTemplate.bind({}); -const DEFAULT_SETTINGS = columns.map((x) => ({id: x.id, isSelected: true})); +export const DEFAULT_SETTINGS = columns.map((x) => ({id: x.id, isSelected: true})); // --------------------------------- const WithTableSettingsTemplate: StoryFn> = (args, context) => { const [settings, setSettings] = React.useState(DEFAULT_SETTINGS); diff --git a/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx b/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx index 59b73ee674..90c98fcbff 100644 --- a/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx +++ b/src/components/Table/__stories__/WithTableSettingsCustomActions/WithTableSettingsCustomActions.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import {ArrowRotateLeft} from '@gravity-ui/icons'; import _isEqual from 'lodash/isEqual'; diff --git a/src/components/Table/__stories__/utils.tsx b/src/components/Table/__stories__/utils.tsx index c16087eb96..b1ec2e306a 100644 --- a/src/components/Table/__stories__/utils.tsx +++ b/src/components/Table/__stories__/utils.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {withTableSelection} from '..'; import {Table} from '../Table'; import type {TableColumnConfig} from '../Table'; diff --git a/src/components/Table/__tests__/Table.hocs.test.ts b/src/components/Table/__tests__/Table.hocs.test.ts index 551d964331..cb85a55089 100644 --- a/src/components/Table/__tests__/Table.hocs.test.ts +++ b/src/components/Table/__tests__/Table.hocs.test.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Table/__tests__/Table.test.tsx b/src/components/Table/__tests__/Table.test.tsx index 60b580eb2a..021a4732e1 100644 --- a/src/components/Table/__tests__/Table.test.tsx +++ b/src/components/Table/__tests__/Table.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type * as React from 'react'; import userEvent from '@testing-library/user-event'; diff --git a/src/components/Table/__tests__/Table.visual.test.tsx b/src/components/Table/__tests__/Table.visual.test.tsx index 8138313b91..95cce9f49d 100644 --- a/src/components/Table/__tests__/Table.visual.test.tsx +++ b/src/components/Table/__tests__/Table.visual.test.tsx @@ -1,19 +1,203 @@ -import React from 'react'; +import {expect} from '@playwright/experimental-ct-react'; -import {test} from '~playwright/core'; +import {smokeTest, test} from '~playwright/core'; -import {TableStories} from './helpersPlaywright'; +import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; -test.describe('Table', () => { +import type {TestTableColumnConfig} from './cases'; +import { + columnAlignCases, + columnStickyCases, + columnWidthCases, + edgePaddingCases, + placeholderCases, + rowDescriptorCases, + verticalAlignCases, + wordWrapCases, +} from './cases'; +import type {TestTableProps} from './helpersPlaywright'; +import { + TableStories, + TestTable, + TestTableWithCustomColumnConfig, + TestTableWithSettings, +} from './helpersPlaywright'; + +test.describe('Table', {tag: '@Table'}, () => { test('render story: ', async ({mount, expectScreenshot}) => { await mount(); - await expectScreenshot(); + await expectScreenshot({ + themes: ['light'], + }); + }); + + test('render story: ', async ({mount, expectScreenshot}) => { + await mount(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + test('render story: ', async ({mount, expectScreenshot}) => { + await mount(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + test('render story: ', async ({mount, page, expectScreenshot}) => { + const size = page.viewportSize(); + if (size) { + await page.setViewportSize({ + width: 1000, + height: size.height, + }); + } + + await mount(, { + width: 'auto', + }); + + await expectScreenshot({ + themes: ['light'], + }); }); test('render story: ', async ({mount, expectScreenshot}) => { await mount(); - await expectScreenshot(); + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + {}, + { + edgePadding: edgePaddingCases, + verticalAlign: verticalAlignCases, + wordWrap: wordWrapCases, + getRowDescriptor: rowDescriptorCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('column config', async ({mount, expectScreenshot}) => { + const smokeScenarios = createSmokeScenarios( + {}, + { + align: columnAlignCases, + sticky: columnStickyCases, + width: columnWidthCases, + placeholder: placeholderCases, + }, + ); + + await mount( +
+ {smokeScenarios.map(([title, props]) => ( +
+

{title}

+
+ +
+
+ ))} +
, + ); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('with copy', async ({mount, expectScreenshot}) => { + const root = await mount(); + + await root.locator('.g-table__copy').locator('nth=0').hover(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('with actions', async ({mount, page, expectScreenshot}) => { + const root = await mount(); + + await root.locator('button').locator('nth=0').click(); + + await expectScreenshot({ + themes: ['light'], + component: page.locator('body'), + }); + }); + + smokeTest('with checkbox', async ({mount, expectScreenshot}) => { + await mount(); + + await expectScreenshot({ + themes: ['light'], + }); + }); + + smokeTest('with settings', async ({mount, page, expectScreenshot}) => { + const root = await mount(); + + await root.locator('button').locator('nth=0').click(); + await expect(page.locator('.g-popup')).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + component: root, + }); + }); + + smokeTest('with table settings', async ({mount, page, expectScreenshot}) => { + const root = await mount(); + + await root.locator('button').locator('nth=0').click(); + await expect(page.locator('.g-popup')).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + component: root, + }); + }); + + smokeTest('with filterable settings', async ({mount, page, expectScreenshot}) => { + const root = await mount( +
+ +
, + ); + + await root.locator('button').locator('nth=0').click(); + await expect(page.locator('.g-popup')).toBeVisible(); + + await expectScreenshot({ + themes: ['light'], + component: root, + }); }); }); diff --git a/src/components/Table/__tests__/Table.withTableSettings.test.tsx b/src/components/Table/__tests__/Table.withTableSettings.test.tsx index ba8c70ee18..4785d56cd6 100644 --- a/src/components/Table/__tests__/Table.withTableSettings.test.tsx +++ b/src/components/Table/__tests__/Table.withTableSettings.test.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import userEvent from '@testing-library/user-event'; import {fireEvent, render, screen, waitFor} from '../../../../test-utils/utils'; diff --git a/src/components/Table/__tests__/cases.tsx b/src/components/Table/__tests__/cases.tsx new file mode 100644 index 0000000000..9cb9b57f53 --- /dev/null +++ b/src/components/Table/__tests__/cases.tsx @@ -0,0 +1,42 @@ +import type {Cases, CasesWithName} from '../../../stories/tests-factory/models'; +import type {DescriptorType, TableColumnConfig, TableProps} from '../Table'; + +import type {DataItem} from './utils'; + +export type TestTableColumnConfig = Partial>; + +export const columnAlignCases: Cases['align']> = [ + 'start', + 'end', + 'center', + 'left', + 'right', +]; + +export const columnStickyCases: Cases['sticky']> = [ + 'start', + 'end', + 'left', + 'right', +]; + +export const columnWidthCases: Cases['width']> = [200]; + +export const placeholderCases: Cases['placeholder']> = ['empty']; + +export const edgePaddingCases: Cases['edgePadding']> = [true]; + +export const verticalAlignCases: Cases['verticalAlign']> = ['top', 'middle']; + +export const wordWrapCases: Cases['wordWrap']> = [true]; + +export const rowDescriptorCases: CasesWithName['getRowDescriptor']> = [ + [ + 'disabled', + (): DescriptorType => { + return { + id: `${Math.random()}`, + }; + }, + ], +]; diff --git a/src/components/Table/__tests__/helpersPlaywright.tsx b/src/components/Table/__tests__/helpersPlaywright.tsx index 97d0a0ef78..13ac6cd62a 100644 --- a/src/components/Table/__tests__/helpersPlaywright.tsx +++ b/src/components/Table/__tests__/helpersPlaywright.tsx @@ -1,5 +1,63 @@ +import * as React from 'react'; + import {composeStories} from '@storybook/react'; +import type {TableColumnConfig, TableProps} from '../Table'; +import {Table} from '../Table'; import * as DefaultTableStories from '../__stories__/Table.stories'; +import {DEFAULT_SETTINGS} from '../__stories__/Table.stories'; +import type {TableSettingsData} from '../hoc'; +import {withTableSettings} from '../hoc'; + +import type {DataItem} from './utils'; +import {columns, data} from './utils'; export const TableStories = composeStories(DefaultTableStories); + +export type TestTableProps = Partial>; + +export const TestTableWithCustomColumnConfig = (props: { + columnConfig: Partial>; +}) => { + const {columnConfig} = props; + + const customColumnConfig = columns.map((item) => { + return { + ...item, + ...columnConfig, + }; + }); + + return ; +}; + +export const TestTable = (props: Partial>) => { + return
; +}; + +const TableWithSettings = withTableSettings({ + width: 200, + sortable: false, + filterable: false, +})(Table); + +export const TestTableWithSettings = (props: Partial>) => { + const customColumnConfig = columns.map((item) => { + return { + ...item, + meta: {sort: true}, + }; + }); + + const [settings, setSettings] = React.useState(DEFAULT_SETTINGS); + + return ( + + ); +}; diff --git a/src/components/Table/hoc/withTableActions/withTableActions.tsx b/src/components/Table/hoc/withTableActions/withTableActions.tsx index c00219c35c..652c3977b6 100644 --- a/src/components/Table/hoc/withTableActions/withTableActions.tsx +++ b/src/components/Table/hoc/withTableActions/withTableActions.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Ellipsis} from '@gravity-ui/icons'; import _memoize from 'lodash/memoize'; diff --git a/src/components/Table/hoc/withTableCopy/withTableCopy.tsx b/src/components/Table/hoc/withTableCopy/withTableCopy.tsx index e13e788752..e44de0c6ad 100644 --- a/src/components/Table/hoc/withTableCopy/withTableCopy.tsx +++ b/src/components/Table/hoc/withTableCopy/withTableCopy.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import _memoize from 'lodash/memoize'; diff --git a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx index 7833dd5bd2..c899bc5db3 100644 --- a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx +++ b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import _difference from 'lodash/difference'; import _get from 'lodash/get'; diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx index 840921b0a7..ddb246321e 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Gear, Grip, Lock} from '@gravity-ui/icons'; import {DragDropContext, Draggable, Droppable} from 'react-beautiful-dnd'; diff --git a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx index a9d1005a60..87adf8ce6f 100644 --- a/src/components/Table/hoc/withTableSettings/withTableSettings.tsx +++ b/src/components/Table/hoc/withTableSettings/withTableSettings.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import {Gear} from '@gravity-ui/icons'; import _get from 'lodash/get'; diff --git a/src/components/Table/hoc/withTableSorting/SortIndicator/SortIndicator.tsx b/src/components/Table/hoc/withTableSorting/SortIndicator/SortIndicator.tsx index ecceb3cc56..eb2981f7b4 100644 --- a/src/components/Table/hoc/withTableSorting/SortIndicator/SortIndicator.tsx +++ b/src/components/Table/hoc/withTableSorting/SortIndicator/SortIndicator.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import {ArrowDown, ArrowUp, ArrowUpArrowDown} from '@gravity-ui/icons'; import {Icon} from '../../../../Icon'; diff --git a/src/components/Table/hoc/withTableSorting/withTableSorting.tsx b/src/components/Table/hoc/withTableSorting/withTableSorting.tsx index a36e23c6d4..e8ff213feb 100644 --- a/src/components/Table/hoc/withTableSorting/withTableSorting.tsx +++ b/src/components/Table/hoc/withTableSorting/withTableSorting.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import * as React from 'react'; import _get from 'lodash/get'; import _memoize from 'lodash/memoize'; diff --git a/src/components/TableColumnSetup/TableColumnSetup.tsx b/src/components/TableColumnSetup/TableColumnSetup.tsx index f233343651..d529969060 100644 --- a/src/components/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/TableColumnSetup/TableColumnSetup.tsx @@ -1,6 +1,6 @@ 'use client'; -import React from 'react'; +import type * as React from 'react'; import {Gear} from '@gravity-ui/icons'; diff --git a/src/components/Tabs/README-ru.md b/src/components/Tabs/README-ru.md new file mode 100644 index 0000000000..7c19a9ba9e --- /dev/null +++ b/src/components/Tabs/README-ru.md @@ -0,0 +1,325 @@ + + +# Tabs + + + +```tsx +import {Tabs} from '@gravity-ui/uikit'; +``` + +Компонент `Tabs` используется организации контента и навигации по нему, а также для переключения между различными представлениями. + +## Элементы + +Для рендеринга элементов `Tabs` используйте свойство `items`. + + + + + +```tsx +const [activeTab, setActiveTab] = React.useState('second'); + +return ( + setActiveTab(tabId)} + items={[ + {id: 'first', title: 'First Tab'}, + {id: 'second', title: 'Second Tab'}, + {id: 'third', title: 'Disabled Tab', disabled: true}, + ]} + /> +); +``` + + + +`Tabs` также имеет специальный компонент для рендеринга отдельных элементов. + + + +```tsx +const [activeTab, setActiveTab] = React.useState('second'); + +return ( + + setActiveTab(tabId)} /> + setActiveTab(tabId)} /> + +); +``` + + + +## Размер + +Размер `Tabs` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`. + + + + + +```tsx + + + + + +``` + + + +## Tabs.Item + +Используется для рендеринга элемента `Tabs`. + +### Иконка + +Используется, если нужно отобразить иконку для элемента `Tabs`. + + + + + +```tsx + + } + id="first" + title="Tab with icon" + onClick={() => {}} + /> + {}} /> + +``` + + + +### Состояния + +Элементы `Tabs` поддерживают флаг `disabled`. + + + + + +```tsx + + {}} /> + {}} /> + +``` + + + +### Счетчик + +Используется, если нужно отобразить число для элемента `Tabs`. + + + + + +```tsx + + {}} counter={13} /> + {}} counter={3} /> + +``` + + + +### Свойства `Tabs.Item` + +| Имя | Описание | Тип | Значение по умолчанию | +| :------- | -------------------------------- | :------------------------: | :-------------------: | +| id | Идентификатор вкладки. | `string` | | +| title | Заголовок вкладки. | `string` `React.ReactNode` | | +| meta | Описание вкладки. | `string` | | +| hint | HTML-атрибут `title`. | `string` | | +| icon | Иконка, отображаемая в начале. | `React.ReactNode` | | +| counter | Число, отображаемое в конце. | `React.ReactNode` | | +| label | `