diff --git a/CODEOWNERS b/CODEOWNERS index b919e8a4cd..297d87abd7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -51,7 +51,7 @@ /src/components/Stories @DarkGenius /src/components/Switch @zamkovskaya /src/components/Table @Raubzeug -/src/components/Tabs @sofiushko +/src/components/tabs @sofiushko /src/components/Text @IsaevAlexandr /src/components/TreeList @IsaevAlexandr /src/components/TreeSelect @IsaevAlexandr diff --git a/src/components/index.ts b/src/components/index.ts index aff2a20ff4..91b3dc5a0a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -51,7 +51,7 @@ export * from './Spin'; export * from './Switch'; export * from './Table'; export * from './TableColumnSetup'; -export * from './Tabs'; +export * from './tabs'; export * from './Text'; export * from './Toaster'; export * from './Toc'; diff --git a/src/components/Tabs/README-ru.md b/src/components/legacy/Tabs/README-ru.md similarity index 99% rename from src/components/Tabs/README-ru.md rename to src/components/legacy/Tabs/README-ru.md index 7c19a9ba9e..cd23c133b3 100644 --- a/src/components/Tabs/README-ru.md +++ b/src/components/legacy/Tabs/README-ru.md @@ -5,7 +5,7 @@ ```tsx -import {Tabs} from '@gravity-ui/uikit'; +import {Tabs} from '@gravity-ui/uikit/legacy'; ``` Компонент `Tabs` используется организации контента и навигации по нему, а также для переключения между различными представлениями. diff --git a/src/components/Tabs/README.md b/src/components/legacy/Tabs/README.md similarity index 99% rename from src/components/Tabs/README.md rename to src/components/legacy/Tabs/README.md index 901c4b62c5..d16af41020 100644 --- a/src/components/Tabs/README.md +++ b/src/components/legacy/Tabs/README.md @@ -5,7 +5,7 @@ ```tsx -import {Tabs} from '@gravity-ui/uikit'; +import {Tabs} from '@gravity-ui/uikit/legacy'; ``` The `Tabs` component is used to explore and organize content, as well as to switch across various views. diff --git a/src/components/Tabs/Tabs.scss b/src/components/legacy/Tabs/Tabs.scss similarity index 98% rename from src/components/Tabs/Tabs.scss rename to src/components/legacy/Tabs/Tabs.scss index f81579f5e3..76e2f096bb 100644 --- a/src/components/Tabs/Tabs.scss +++ b/src/components/legacy/Tabs/Tabs.scss @@ -1,7 +1,7 @@ -@use '../variables'; -@use '../../../styles/mixins'; +@use '../../variables'; +@use '../../../../styles/mixins'; -$block: '.#{variables.$ns}tabs'; +$block: '.#{variables.$ns}tabs-legacy'; #{$block} { --_--vertical-item-padding: var(--g-tabs-vertical-item-padding, 6px 20px); diff --git a/src/components/Tabs/Tabs.tsx b/src/components/legacy/Tabs/Tabs.tsx similarity index 94% rename from src/components/Tabs/Tabs.tsx rename to src/components/legacy/Tabs/Tabs.tsx index 375fcaaa1e..3591ef2694 100644 --- a/src/components/Tabs/Tabs.tsx +++ b/src/components/legacy/Tabs/Tabs.tsx @@ -2,9 +2,9 @@ import * as React from 'react'; -import type {AriaLabelingProps, QAProps} from '../types'; -import {block} from '../utils/cn'; -import {filterDOMProps} from '../utils/filterDOMProps'; +import type {AriaLabelingProps, QAProps} from '../../types'; +import {block} from '../../utils/cn'; +import {filterDOMProps} from '../../utils/filterDOMProps'; import {TabsContext} from './TabsContext'; import {TabsItem} from './TabsItem'; @@ -12,7 +12,7 @@ import type {TabsItemProps as TabsItemInternalProps} from './TabsItem'; import './Tabs.scss'; -const b = block('tabs'); +const b = block('tabs-legacy'); export enum TabsDirection { Horizontal = 'horizontal', @@ -66,6 +66,9 @@ const getActiveTabId = ( const emptyTabsList: TabsItemProps[] = []; +/** + * @deprecated + */ const TabsComponent = React.forwardRef( ( { diff --git a/src/components/Tabs/TabsContext.ts b/src/components/legacy/Tabs/TabsContext.ts similarity index 100% rename from src/components/Tabs/TabsContext.ts rename to src/components/legacy/Tabs/TabsContext.ts diff --git a/src/components/Tabs/TabsItem.tsx b/src/components/legacy/Tabs/TabsItem.tsx similarity index 93% rename from src/components/Tabs/TabsItem.tsx rename to src/components/legacy/Tabs/TabsItem.tsx index 4fcb33bd3f..cf7f91ee8f 100644 --- a/src/components/Tabs/TabsItem.tsx +++ b/src/components/legacy/Tabs/TabsItem.tsx @@ -2,14 +2,14 @@ import * as React from 'react'; -import {Label} from '../Label'; -import type {LabelProps} from '../Label'; -import type {QAProps} from '../types'; -import {block} from '../utils/cn'; +import {Label} from '../../Label'; +import type {LabelProps} from '../../Label'; +import type {QAProps} from '../../types'; +import {block} from '../../utils/cn'; import {TabsContext} from './TabsContext'; -const b = block('tabs'); +const b = block('tabs-legacy'); type ExtraProps = Omit< React.HTMLProps, diff --git a/src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-allow-not-selected-light-chromium-linux.png b/src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-allow-not-selected-light-chromium-linux.png similarity index 100% rename from src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-allow-not-selected-light-chromium-linux.png rename to src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-allow-not-selected-light-chromium-linux.png diff --git a/src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-light-chromium-linux.png b/src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-light-chromium-linux.png similarity index 100% rename from src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-light-chromium-linux.png rename to src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-light-chromium-linux.png diff --git a/src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-with-custom-tab-light-chromium-linux.png b/src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-with-custom-tab-light-chromium-linux.png similarity index 100% rename from src/components/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-with-custom-tab-light-chromium-linux.png rename to src/components/legacy/Tabs/__snapshots__/Tabs.visual.test.tsx-snapshots/Tabs-smoke-with-custom-tab-light-chromium-linux.png diff --git a/src/components/Tabs/__stories__/Docs.mdx b/src/components/legacy/Tabs/__stories__/Docs.mdx similarity index 100% rename from src/components/Tabs/__stories__/Docs.mdx rename to src/components/legacy/Tabs/__stories__/Docs.mdx diff --git a/src/components/Tabs/__stories__/Tabs.stories.tsx b/src/components/legacy/Tabs/__stories__/Tabs.stories.tsx similarity index 98% rename from src/components/Tabs/__stories__/Tabs.stories.tsx rename to src/components/legacy/Tabs/__stories__/Tabs.stories.tsx index 6295e3ad7b..29758759e7 100644 --- a/src/components/Tabs/__stories__/Tabs.stories.tsx +++ b/src/components/legacy/Tabs/__stories__/Tabs.stories.tsx @@ -10,7 +10,7 @@ import {getTabsMock} from './getTabsMock'; import type {StoryParams} from './types'; const meta: Meta = { - title: 'Components/Navigation/Tabs', + title: 'Legacy/Tabs', component: Tabs, args: { direction: TabsDirection.Horizontal, diff --git a/src/components/Tabs/__stories__/getTabsMock.tsx b/src/components/legacy/Tabs/__stories__/getTabsMock.tsx similarity index 100% rename from src/components/Tabs/__stories__/getTabsMock.tsx rename to src/components/legacy/Tabs/__stories__/getTabsMock.tsx diff --git a/src/components/Tabs/__stories__/types.ts b/src/components/legacy/Tabs/__stories__/types.ts similarity index 100% rename from src/components/Tabs/__stories__/types.ts rename to src/components/legacy/Tabs/__stories__/types.ts diff --git a/src/components/Tabs/__tests__/Tabs.test.tsx b/src/components/legacy/Tabs/__tests__/Tabs.test.tsx similarity index 82% rename from src/components/Tabs/__tests__/Tabs.test.tsx rename to src/components/legacy/Tabs/__tests__/Tabs.test.tsx index 63c7c913c7..1c56e709b2 100644 --- a/src/components/Tabs/__tests__/Tabs.test.tsx +++ b/src/components/legacy/Tabs/__tests__/Tabs.test.tsx @@ -2,7 +2,7 @@ import type * as React from 'react'; import userEvent from '@testing-library/user-event'; -import {render, screen} from '../../../../test-utils/utils'; +import {render, screen} from '../../../../../test-utils/utils'; import {Tabs, TabsDirection} from '../Tabs'; import type {TabsItemProps, TabsSize} from '../Tabs'; @@ -21,8 +21,8 @@ test('should render tabs by default', () => { const component = screen.getByRole('tablist'); expect(component).toBeVisible(); - expect(component).toHaveClass('g-tabs_size_m'); - expect(component).toHaveClass('g-tabs_direction_horizontal'); + expect(component).toHaveClass('g-tabs-legacy_size_m'); + expect(component).toHaveClass('g-tabs-legacy_direction_horizontal'); }); test('should not render tabs if no items', () => { @@ -38,7 +38,7 @@ test.each(new Array('m', 'l', 'xl'))('should render with given "%s" si render(); const component = screen.getByTestId(qaId); - expect(component).toHaveClass(`g-tabs_size_${size}`); + expect(component).toHaveClass(`g-tabs-legacy_size_${size}`); }); test.each(new Array(TabsDirection.Horizontal, TabsDirection.Vertical))( @@ -47,7 +47,7 @@ test.each(new Array(TabsDirection.Horizontal, TabsDirection.Verti render(); const component = screen.getByTestId(qaId); - expect(component).toHaveClass(`g-tabs_direction_${direction}`); + expect(component).toHaveClass(`g-tabs-legacy_direction_${direction}`); }, ); @@ -65,10 +65,10 @@ test('should not select tab if allow not selected', () => { const tabComponent1 = screen.getByTitle(tabTitle1); const tabComponent2 = screen.getByTitle(tabTitle2); - expect(tabComponent1).not.toHaveClass('g-tabs__item_active'); + expect(tabComponent1).not.toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent1).toHaveAttribute('aria-selected', 'false'); - expect(tabComponent2).not.toHaveClass('g-tabs__item_active'); + expect(tabComponent2).not.toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent2).toHaveAttribute('aria-selected', 'false'); }); @@ -77,10 +77,10 @@ test('should select first tab as active', () => { const tabComponent1 = screen.getByTitle(tabTitle1); const tabComponent2 = screen.getByTitle(tabTitle2); - expect(tabComponent1).toHaveClass('g-tabs__item_active'); + expect(tabComponent1).toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent1).toHaveAttribute('aria-selected', 'true'); - expect(tabComponent2).not.toHaveClass('g-tabs__item_active'); + expect(tabComponent2).not.toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent2).toHaveAttribute('aria-selected', 'false'); }); @@ -89,10 +89,10 @@ test('should passed active tab', () => { const tabComponent1 = screen.getByTitle(tabTitle1); const tabComponent2 = screen.getByTitle(tabTitle2); - expect(tabComponent1).not.toHaveClass('g-tabs__item_active'); + expect(tabComponent1).not.toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent1).toHaveAttribute('aria-selected', 'false'); - expect(tabComponent2).toHaveClass('g-tabs__item_active'); + expect(tabComponent2).toHaveClass('g-tabs-legacy__item_active'); expect(tabComponent2).toHaveAttribute('aria-selected', 'true'); }); diff --git a/src/components/Tabs/__tests__/Tabs.visual.test.tsx b/src/components/legacy/Tabs/__tests__/Tabs.visual.test.tsx similarity index 96% rename from src/components/Tabs/__tests__/Tabs.visual.test.tsx rename to src/components/legacy/Tabs/__tests__/Tabs.visual.test.tsx index d4acb7dfd9..bd5a4c57fe 100644 --- a/src/components/Tabs/__tests__/Tabs.visual.test.tsx +++ b/src/components/legacy/Tabs/__tests__/Tabs.visual.test.tsx @@ -1,6 +1,6 @@ import {smokeTest, test} from '~playwright/core'; -import {createSmokeScenarios} from '../../../stories/tests-factory/create-smoke-scenarios'; +import {createSmokeScenarios} from '../../../../stories/tests-factory/create-smoke-scenarios'; import type {TabsProps} from '../Tabs'; import {directionCases, sizeCases} from './cases'; diff --git a/src/components/Tabs/__tests__/TabsItem.test.tsx b/src/components/legacy/Tabs/__tests__/TabsItem.test.tsx similarity index 92% rename from src/components/Tabs/__tests__/TabsItem.test.tsx rename to src/components/legacy/Tabs/__tests__/TabsItem.test.tsx index aa44254082..89a7888a63 100644 --- a/src/components/Tabs/__tests__/TabsItem.test.tsx +++ b/src/components/legacy/Tabs/__tests__/TabsItem.test.tsx @@ -1,7 +1,7 @@ import {Flame} from '@gravity-ui/icons'; import userEvent from '@testing-library/user-event'; -import {render, screen} from '../../../../test-utils/utils'; +import {render, screen} from '../../../../../test-utils/utils'; import {TabsItem} from '../TabsItem'; const tabId = 'tab-id'; @@ -12,7 +12,7 @@ test('should render tab item by default', () => { const component = screen.getByRole('tab'); expect(component).toBeVisible(); - expect(component).not.toHaveClass('g-tabs__item_active'); + expect(component).not.toHaveClass('g-tabs-legacy__item_active'); expect(component).toHaveAttribute('aria-selected', 'false'); expect(component).toHaveAttribute('aria-disabled', 'false'); }); @@ -22,7 +22,7 @@ test('should render active tab item', () => { const component = screen.getByRole('tab'); expect(component).toBeVisible(); - expect(component).toHaveClass('g-tabs__item_active'); + expect(component).toHaveClass('g-tabs-legacy__item_active'); expect(component).toHaveAttribute('aria-selected', 'true'); }); @@ -70,7 +70,7 @@ test('should render id if title is empty', () => { const titleComponent = screen.getByText(tabId); expect(component).toContainElement(titleComponent); - expect(titleComponent).toHaveClass('g-tabs__item-title'); + expect(titleComponent).toHaveClass('g-tabs-legacy__item-title'); }); test('should render counter', () => { @@ -82,7 +82,7 @@ test('should render counter', () => { const counterComponent = screen.getByText(counter); expect(counterComponent).toBeVisible(); - expect(counterComponent).toHaveClass('g-tabs__item-counter'); + expect(counterComponent).toHaveClass('g-tabs-legacy__item-counter'); expect(component).toContainElement(counterComponent); }); @@ -126,7 +126,7 @@ test('should render meta', () => { const metaComponent = screen.getByText(meta); expect(metaComponent).toBeVisible(); - expect(metaComponent).toHaveClass('g-tabs__item-meta'); + expect(metaComponent).toHaveClass('g-tabs-legacy__item-meta'); expect(component).toContainElement(metaComponent); }); diff --git a/src/components/Tabs/__tests__/cases.tsx b/src/components/legacy/Tabs/__tests__/cases.tsx similarity index 79% rename from src/components/Tabs/__tests__/cases.tsx rename to src/components/legacy/Tabs/__tests__/cases.tsx index 5de47fd6b1..b31fd9f89e 100644 --- a/src/components/Tabs/__tests__/cases.tsx +++ b/src/components/legacy/Tabs/__tests__/cases.tsx @@ -1,4 +1,4 @@ -import type {Cases} from '../../../stories/tests-factory/models'; +import type {Cases} from '../../../../stories/tests-factory/models'; import type {TabsProps} from '../Tabs'; import {TabsDirection} from '../Tabs'; diff --git a/src/components/Tabs/__tests__/helpers.tsx b/src/components/legacy/Tabs/__tests__/helpers.tsx similarity index 100% rename from src/components/Tabs/__tests__/helpers.tsx rename to src/components/legacy/Tabs/__tests__/helpers.tsx diff --git a/src/components/Tabs/index.ts b/src/components/legacy/Tabs/index.ts similarity index 100% rename from src/components/Tabs/index.ts rename to src/components/legacy/Tabs/index.ts diff --git a/src/components/legacy/index.ts b/src/components/legacy/index.ts index 8f473de4b9..5a82c4ebfd 100644 --- a/src/components/legacy/index.ts +++ b/src/components/legacy/index.ts @@ -1 +1,2 @@ export * from './Popover'; +export * from './Tabs'; diff --git a/src/components/tabs/README.md b/src/components/tabs/README.md new file mode 100644 index 0000000000..91530bfceb --- /dev/null +++ b/src/components/tabs/README.md @@ -0,0 +1,360 @@ + + +# Tabs components + + + +```tsx +import {TabProvider, TabList, Tab, TabPanel} from '@gravity-ui/uikit'; +``` + +Tabs components is used to explore, organize content and switch between different views. + + + + + +```tsx +const [activeTab, setActiveTab] = React.useState('second'); + +return ( + + + + First Tab + + + Active Tab + + + Disabled Tab + + +
+ First Panel + Second Panel + Third Panel +
+
+); +``` + + + +### Components + +- [TabProvider](#tabprovider) +- [TabList](#tablist) +- [Tab](#tab) +- [TabPanel](#tabpanel) + +## TabProvider + +A component that provides the tab selection functionality + +### Properties + +| Name | Description | Type | Default | +| :------- | :------------------------------------------------------- | :-------------------: | :-----: | +| children | List of tabs and tab panels, probably with some wrappers | `React.ReactNode` | | +| value | Active tab value | `string \| undefined` | | + +## TabList + +Component that serves as the container for a set of `tabs` + +### Size + +To control the size of the `tabs` use the `size` property. Default size is `m`. + + + + + +```tsx + + M Size first + M Size second + + + L Size first + L Size second + + + XL Size first + v Size second + +``` + + + +### Properties + +| Name | Description | Type | Default | +| :-------------: | :---------------------------------------- | :------------------------------: | :------: | +| value | Active tab value | `string \| undefined` | | +| children | List of tabs, probably with some wrappers | `React.ReactNode` | | +| onUpdate | Update tab handler | `onUpdate?(value: string): void` | | +| className | CSS-class of element | `string \| undefined` | | +| size | Element size | `'m' \| 'l' \| 'xl'` | `'m'` | +| contentOverflow | Controls component overflow behavior | `'wrap'` | `'wrap'` | +| qa | HTML `data-qa` attribute, used in tests | `string` | | + +## Tab + +This component is used to render tab items. + +### Icon + +Used if you need to display an icon for a tab item. + + + + + +```tsx + + }> + Tab with icon + + Tab without icon + +``` + + + +### States + +Tab item has disabled flag. + + + + + +```tsx + + First Tab + + Disabled Tab + + +``` + + + +### Counter + +Used if you need to display a number for a tabs item. + + + + + +```tsx + + + First Tab + + + Second Tab + + +``` + + + +### Label + +Used if you need to display a label for a tabs item. + + + + + +```tsx + + + First Tab + + + Second Tab + + +``` + + + +### Properties + +| Name | Description | Type | Default | +| :------------ | ---------------------------------------------------------------- | :-------------------: | :-----: | +| value | Tab value | `string` | | +| title | Tab title | `string \| undefined` | | +| icon | Icon displayed at the start | `React.ReactNode` | | +| counter | Content displayed at the end | `number \| string` | | +| href | A URL to link to. | `string \| undefined` | | +| label | `