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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {Decorator, Preview} from '@storybook/react';
import {ThemeProvider, configure} from '@gravity-ui/uikit';
import React from 'react';

import '@gravity-ui/uikit/styles/fonts.css';
import '@gravity-ui/uikit/styles/styles.css';
import '@gravity-ui/unipika/dist/unipika.css';
import '@gravity-ui/illustrations/styles/styles.scss';
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ For the full ownership rules, examples, and review checklist, see [`plans/styles

## i18n

Every widget/module/component with its own reusable scenario keeps its localization in an `i18n/` subfolder (`en.json`, `ru.json`, `dicts.ts`, `index.ts`), registers its keyset via [`addI18Keysets`](src/i18n/index.ts:11) with a name like `` `qp:widget-name` ``, and uses the typed `t`/`i18n(...)` function — see the example in [`QueriesHistory`](src/widgets/QueriesHistory/i18n/index.ts:1).
Every widget/module/component with its own reusable scenario keeps its localization in an `i18n/` subfolder (`en.json`, `ru.json`, `dicts.ts`, `index.ts`), registers its keyset via [`addI18Keysets`](src/i18n/index.ts:11) with a name like `` `qp:widget-name` ``, and uses the typed `t`/`i18n(...)` function — see the example in [`QueriesHistory`](src/modules/QueriesHistory/i18n/index.ts:1).

Brief naming notes:

Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,25 @@ Prefer widgets for product screens. Use modules and components when you need a c

## Usage

### Widget imports
### Individual imports

Widgets support both root and individual imports:
Widgets and modules support both root and individual imports:

```tsx
import {SavedQueries} from '@gravity-ui/querieskit';
```

Alternatively, start directly from the widget's entrypoint:
Alternatively, start directly from the module's entrypoint:

```tsx
import {SavedQueries} from '@gravity-ui/querieskit/widgets/SavedQueries';
import type {SavedQueriesProps} from '@gravity-ui/querieskit/widgets/SavedQueries';
import {SavedQueries} from '@gravity-ui/querieskit/modules/SavedQueries';
import type {SavedQueriesProps} from '@gravity-ui/querieskit/modules/SavedQueries';
```

Individual entrypoints are available for `QueriesHistory`, `SavedQueries`,
`TutorialsHistory`, `QueriesNavigation`, `QueryExecutionPanel`, and `DashboardCharts`.
They expose each widget's existing public exports, including its props and helpers.
`TutorialsHistory`, `QueriesNavigation` under `modules/`, and `QueriesSidebar`,
`QueryExecutionPanel`, `DashboardCharts` under `widgets/`.
They expose each unit's existing public exports, including its props and helpers.
Shared data types remain available from the package root.

Individual imports limit the dependency graph the bundler needs to traverse.
Expand All @@ -85,6 +86,13 @@ Some bundlers, including esbuild, retain CSS from unused root re-exports even wh
their JavaScript is removed. Individual widget imports avoid introducing those
unrelated styles.

### QueriesSidebar

Combines history, saved queries, navigation and tutorials in one panel. Supply
an ordered `tabs` array and an optional `header` for product selection or actions.
Use `hideTabs` with `activeTab` when navigation lives outside the sidebar.
See [the API and migration guide](src/widgets/QueriesSidebar/README.md).

### QueryExecutionPanel

A query execution panel with ordered, typed tabs, custom content, automatic tab
Expand Down
82 changes: 66 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,42 +28,42 @@
"./package.json": "./package.json",
"./widgets/QueriesHistory": {
"import": {
"types": "./build/esm/widgets/QueriesHistory/index.d.ts",
"default": "./build/esm/widgets/QueriesHistory/index.js"
"types": "./build/esm/modules/QueriesHistory/index.d.ts",
"default": "./build/esm/modules/QueriesHistory/index.js"
},
"require": {
"types": "./build/cjs/widgets/QueriesHistory/index.d.ts",
"default": "./build/cjs/widgets/QueriesHistory/index.js"
"types": "./build/cjs/modules/QueriesHistory/index.d.ts",
"default": "./build/cjs/modules/QueriesHistory/index.js"
}
},
"./widgets/SavedQueries": {
"import": {
"types": "./build/esm/widgets/SavedQueries/index.d.ts",
"default": "./build/esm/widgets/SavedQueries/index.js"
"types": "./build/esm/modules/SavedQueries/index.d.ts",
"default": "./build/esm/modules/SavedQueries/index.js"
},
"require": {
"types": "./build/cjs/widgets/SavedQueries/index.d.ts",
"default": "./build/cjs/widgets/SavedQueries/index.js"
"types": "./build/cjs/modules/SavedQueries/index.d.ts",
"default": "./build/cjs/modules/SavedQueries/index.js"
}
},
"./widgets/TutorialsHistory": {
"import": {
"types": "./build/esm/widgets/TutorialsHistory/index.d.ts",
"default": "./build/esm/widgets/TutorialsHistory/index.js"
"types": "./build/esm/modules/TutorialsHistory/index.d.ts",
"default": "./build/esm/modules/TutorialsHistory/index.js"
},
"require": {
"types": "./build/cjs/widgets/TutorialsHistory/index.d.ts",
"default": "./build/cjs/widgets/TutorialsHistory/index.js"
"types": "./build/cjs/modules/TutorialsHistory/index.d.ts",
"default": "./build/cjs/modules/TutorialsHistory/index.js"
}
},
"./widgets/QueriesNavigation": {
"import": {
"types": "./build/esm/widgets/QueriesNavigation/index.d.ts",
"default": "./build/esm/widgets/QueriesNavigation/index.js"
"types": "./build/esm/modules/QueriesNavigation/index.d.ts",
"default": "./build/esm/modules/QueriesNavigation/index.js"
},
"require": {
"types": "./build/cjs/widgets/QueriesNavigation/index.d.ts",
"default": "./build/cjs/widgets/QueriesNavigation/index.js"
"types": "./build/cjs/modules/QueriesNavigation/index.d.ts",
"default": "./build/cjs/modules/QueriesNavigation/index.js"
}
},
"./widgets/DashboardCharts": {
Expand Down Expand Up @@ -95,6 +95,56 @@
"types": "./build/cjs/widgets/QueryExecutionPanel/index.d.ts",
"default": "./build/cjs/widgets/QueryExecutionPanel/index.js"
}
},
"./modules/QueriesHistory": {
"import": {
"types": "./build/esm/modules/QueriesHistory/index.d.ts",
"default": "./build/esm/modules/QueriesHistory/index.js"
},
"require": {
"types": "./build/cjs/modules/QueriesHistory/index.d.ts",
"default": "./build/cjs/modules/QueriesHistory/index.js"
}
},
"./modules/QueriesNavigation": {
"import": {
"types": "./build/esm/modules/QueriesNavigation/index.d.ts",
"default": "./build/esm/modules/QueriesNavigation/index.js"
},
"require": {
"types": "./build/cjs/modules/QueriesNavigation/index.d.ts",
"default": "./build/cjs/modules/QueriesNavigation/index.js"
}
},
"./modules/SavedQueries": {
"import": {
"types": "./build/esm/modules/SavedQueries/index.d.ts",
"default": "./build/esm/modules/SavedQueries/index.js"
},
"require": {
"types": "./build/cjs/modules/SavedQueries/index.d.ts",
"default": "./build/cjs/modules/SavedQueries/index.js"
}
},
"./modules/TutorialsHistory": {
"import": {
"types": "./build/esm/modules/TutorialsHistory/index.d.ts",
"default": "./build/esm/modules/TutorialsHistory/index.js"
},
"require": {
"types": "./build/cjs/modules/TutorialsHistory/index.d.ts",
"default": "./build/cjs/modules/TutorialsHistory/index.js"
}
},
"./widgets/QueriesSidebar": {
"import": {
"types": "./build/esm/widgets/QueriesSidebar/index.d.ts",
"default": "./build/esm/widgets/QueriesSidebar/index.js"
},
"require": {
"types": "./build/cjs/widgets/QueriesSidebar/index.d.ts",
"default": "./build/cjs/widgets/QueriesSidebar/index.js"
}
}
},
"files": [
Expand Down
6 changes: 3 additions & 3 deletions plans/i18n-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ widgets/
index.ts
```

- [`dicts.ts`](../src/widgets/QueriesHistory/i18n/dicts.ts:1) — собирает словари по языкам: `export default {en, ru}`.
- [`index.ts`](../src/widgets/QueriesHistory/i18n/index.ts:1) — регистрирует кейсет через [`addI18Keysets`](../src/i18n/index.ts:11) и экспортирует типизированную функцию `t`:
- [`dicts.ts`](../src/modules/QueriesHistory/i18n/dicts.ts:1) — собирает словари по языкам: `export default {en, ru}`.
- [`index.ts`](../src/modules/QueriesHistory/i18n/index.ts:1) — регистрирует кейсет через [`addI18Keysets`](../src/i18n/index.ts:11) и экспортирует типизированную функцию `t`:

```ts
import {addI18Keysets} from '../../../i18n';
Expand All @@ -28,7 +28,7 @@ widgets/
```

- Имя кейсета всегда начинается с префикса `qp:` (namespace библиотеки) — тип `addI18Keysets` требует `` `qp:${string}` ``.
- В компоненте используется как `i18n('key_name')`, см. [`QueriesHistory.tsx`](../src/widgets/QueriesHistory/QueriesHistory.tsx:41).
- В компоненте используется как `i18n('key_name')`, см. [`QueriesHistory.tsx`](../src/modules/QueriesHistory/QueriesHistory.tsx:41).
- `modules` и `components`, у которых есть собственный переиспользуемый сценарий, также могут заводить свою папку `i18n/` по этой же схеме — правило применяется на всех трёх уровнях (`components` / `modules` / `widgets`).

## Кейсеты
Expand Down
4 changes: 3 additions & 1 deletion plans/queries-history-structure.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> Historical plan: QueriesHistory now lives in modules and is composed by QueriesSidebar. See [current architecture](queries-sidebar-architecture.md).

# План трёхуровневой структуры QueriesKit

## Контекст
Expand Down Expand Up @@ -129,7 +131,7 @@ src/
## Предлагаемый порядок реализации

1. Создать папки [`src/components`](src/components), [`src/modules`](src/modules), [`src/widgets`](src/widgets) как публичные уровни библиотеки.
2. Перенести [`QueriesHistory`](src/components/QueriesHistory/QueriesHistory.tsx:28) и его i18n в [`src/widgets/QueriesHistory`](src/widgets/QueriesHistory).
2. Перенести [`QueriesHistory`](src/components/QueriesHistory/QueriesHistory.tsx:28) и его i18n в [`src/modules/QueriesHistory`](src/modules/QueriesHistory).
3. Перенести [`HistoryHeader`](src/components/QueriesHistory/HistoryHeader/HistoryHeader.tsx:13), [`HistoryList`](src/components/QueriesHistory/HistoryList.tsx:26) (вместе с [`HistoryRowContent`](src/components/QueriesHistory/HistoryRowContent.tsx:1) и [`prepareRowData`](src/components/QueriesHistory/helpers/prepareRowData.ts)), [`HistoryRow`](src/components/QueriesHistory/HistoryRow/HistoryRow.tsx:17) в [`src/modules`](src/modules).
4. Вынести [`QueryStatusIcon`](src/components/QueryStatusIcon/QueryStatusIcon.tsx:27), [`QueryDuration`](src/components/QueryDuration/QueryDuration.tsx) (вместе с хуком [`useQueryDuration`](src/components/QueryDuration/useQueryDuration.ts)) и [`HistoryGroupHeader`](src/components/HistoryGroupHeader/HistoryGroupHeader.tsx:1) в [`src/components`](src/components).
5. Оставить [`HistorySearch`](src/components/QueriesHistory/HistoryHeader/HistorySearch.tsx:16), [`HistoryFilter`](src/components/QueriesHistory/HistoryHeader/HistoryFilter.tsx:16), [`HistoryRowHeader`](src/components/QueriesHistory/HistoryRow/HistoryRowHeader.tsx:19), [`HistoryRowMenu`](src/components/QueriesHistory/HistoryRow/HistoryRowMenu.tsx) внутри соответствующих modules, пока нет явной потребности в самостоятельном использовании.
Expand Down
18 changes: 9 additions & 9 deletions plans/queries-navigation-architecture-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

Затронутые единицы:

- Виджет [`QueriesNavigation`](src/widgets/QueriesNavigation/QueriesNavigation.tsx:51) + хелперы
[`createNavigationDetailResolver`](src/widgets/QueriesNavigation/helpers/createNavigationDetailResolver.ts:13),
[`createTableDetailConfig`](src/widgets/QueriesNavigation/helpers/createTableDetailConfig.tsx:45),
[`createEmptyDetailConfig`](src/widgets/QueriesNavigation/helpers/createEmptyDetailConfig.ts:5).
- Виджет [`QueriesNavigation`](src/modules/QueriesNavigation/QueriesNavigation.tsx:51) + хелперы
[`createNavigationDetailResolver`](src/modules/QueriesNavigation/helpers/createNavigationDetailResolver.ts:13),
[`createTableDetailConfig`](src/modules/QueriesNavigation/helpers/createTableDetailConfig.tsx:45),
[`createEmptyDetailConfig`](src/modules/QueriesNavigation/helpers/createEmptyDetailConfig.ts:5).
- Модули: [`NavigationDetail`](src/modules/NavigationDetail/NavigationDetail.tsx:39),
[`NavigationHeader`](src/modules/NavigationHeader/NavigationHeader.tsx:15),
[`ClustersList`](src/modules/ClustersList/ClustersList.tsx:23),
Expand Down Expand Up @@ -41,7 +41,7 @@
раскладка/отступы задаются через `Flex gap` или `--g-spacing-*` в `.scss`. Инлайн-`gap:4` стоит
заменить на `Flex gap` или CSS-класс с токеном. (Инлайн-стили и текстовые заглушки в сторибуках
оставляем как есть — для демо это допустимо.)
- **Пустой `title: ''` в конфиге таба.** В [`createEmptyDetailConfig`](src/widgets/QueriesNavigation/helpers/createEmptyDetailConfig.ts:9)
- **Пустой `title: ''` в конфиге таба.** В [`createEmptyDetailConfig`](src/modules/QueriesNavigation/helpers/createEmptyDetailConfig.ts:9)
создаётся таб с пустым заголовком — формально валидация i18n не срабатывает (строка не идёт
через `t`), но семантически это «фейковый» таб только ради контейнера. См. п.4.

Expand Down Expand Up @@ -105,7 +105,7 @@

Замечания:

- **Дженерик теряется в detail-резолверах.** В [`createTableDetailConfig`](src/widgets/QueriesNavigation/helpers/createTableDetailConfig.tsx:19)
- **Дженерик теряется в detail-резолверах.** В [`createTableDetailConfig`](src/modules/QueriesNavigation/helpers/createTableDetailConfig.tsx:19)
резолверы типизированы как `NavigationSchemaResolver<T>`, но возвращают `NavigationSchemaConfig`
без `<TColumn>`. Из-за этого `extraColumns`-типизация кастомных колонок в конфиге таблицы теряется.
Стоит пробросить `TColumn`/`TRow` до `NavigationSchema data`.
Expand All @@ -117,7 +117,7 @@
индексную сигнатуру `[key: string]: unknown`. Это ослабляет типизацию расширений (лучше решать
через дженерик `T extends ...`, который и так есть). Возможная избыточность — см. п.6.
- **`resolvedDetailActions = detailActions ?? actions`** в
[`QueriesNavigation`](src/widgets/QueriesNavigation/QueriesNavigation.tsx:86): fallback header-экшенов
[`QueriesNavigation`](src/modules/QueriesNavigation/QueriesNavigation.tsx:86): fallback header-экшенов
на detail неочевиден. Стоит задокументировать поведение в типе `NavigationDetailPanelConfig`.

## 5. Дублирование кода
Expand Down Expand Up @@ -164,7 +164,7 @@

- **Повторяющиеся `Loading/Empty/Error`-стори у 4 detail-модулей.** У Schema, Preview, Meta, View
практически идентичные state-стори. Их можно оставить (они полезны для autodocs), но mock-данные
дублируются между сторибуками и [`QueriesNavigation.stories`](src/widgets/QueriesNavigation/QueriesNavigation.stories.tsx:148)
дублируются между сторибуками и [`QueriesNavigation.stories`](src/modules/QueriesNavigation/QueriesNavigation.stories.tsx:148)
(`TABLE_SCHEMA_COLUMNS`, `TABLE_PREVIEW_ROWS`, `TABLE_META_GROUPS`, `TABLE_VIEW_SECTIONS`
повторяют данные модульных сторибуков). Кандидат на общий `story/mockData.ts`
(как уже сделано в `DashboardCharts/story/mockData.ts` и `ChartEditor/story/mockData.ts`).
Expand All @@ -189,7 +189,7 @@
и [`buildViewColumns`](src/modules/NavigationView/helpers/buildViewColumns.tsx:5) в один хелпер.
2. Вынести общий `isEmptyValue` в `src/helpers/`.
3. Пробросить дженерики `TColumn`/`TRow` в резолверах
[`createTableDetailConfig`](src/widgets/QueriesNavigation/helpers/createTableDetailConfig.tsx:45).
[`createTableDetailConfig`](src/modules/QueriesNavigation/helpers/createTableDetailConfig.tsx:45).
4. Решить статус `ClusterRow`/`NavigationItemRow`: поднять в `components` либо убрать из
публичного barrel.

Expand Down
25 changes: 25 additions & 0 deletions plans/queries-sidebar-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# QueriesSidebar architecture

`QueriesSidebar` is the product widget for the shared aside shown in the updated
design. `QueriesHistory`, `SavedQueries`, `QueriesNavigation`, `TutorialsHistory`
are public scenario modules. They remain useful independently and assemble their
existing lower-level modules and components.

The widget owns the optional header slot, ordered icon tabs, controlled or
uncontrolled selection, and retained content panels. It imports concrete module
entrypoints. Modules do not import the widget. Public contracts live in `src/types`.

`hideTabs` supports application-owned navigation: `activeTab` still selects content,
the header remains available, and the same DOM panels change from tab panels to
named regions. UIKit's public Tab component override supplies explicit IDs linking
the visible tabs to these panels, without changing their React identity.

Each panel mounts its content on first visit. `ListActivityContext`, a shared helper
independent of widget types, pauses the pagination sentinel in retained hidden
panels. It does not change list keys or remove callbacks from module props.

The modules keep their own styles, i18n, internal parts and helpers. The sidebar
owns only shell styles and section labels. Root exports are unchanged; new module
entrypoints are canonical and old widget entrypoints are package export aliases.

See the [API and migration guide](../src/widgets/QueriesSidebar/README.md).
5 changes: 5 additions & 0 deletions src/components/LazyList/LazyList.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.qp-lazy-list {
&__item {
// Widen the virtualized rows, keeping both axes in the list's scroll container.
min-width: var(--qp-lazy-list-item-min-width, 0px);
}

.g-list__item_selected:hover {
background-color: var(--g-color-base-selection-hover);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/LazyList/LazyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const LazyList = <T extends object>({
<List<LazyListRow<T>>
ref={listRef}
className={block(null, className)}
itemClassName={block('item')}
filterable={filterable}
items={rows}
itemHeight={getRowHeight}
Expand Down
7 changes: 4 additions & 3 deletions src/components/QueryResultsTable/QueryResultsTable.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
&__cell {
display: block;
padding-right: var(--g-spacing-5);
font-family: var(--g-font-family-monospace);
font-size: var(--g-text-code-2-font-size);
line-height: var(--g-text-code-2-line-height);
font-family: var(--g-text-body-font-family);
font-size: var(--g-text-body-1-font-size);
line-height: var(--g-text-body-1-line-height);
overflow-wrap: anywhere;

&:hover .qp-query-results-table__cell-actions,
Expand All @@ -40,6 +40,7 @@

.unipika {
display: inline;
font: inherit;
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/helpers/ListActivityContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {createContext} from 'react';

/** Pauses automatic list loading while a retained panel is hidden. */
export const ListActivityContext = createContext(true);
Loading
Loading