Skip to content
Open
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
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ jobs:
- name: Build and check widget tree-shaking
run: npm run test:tree-shaking

- name: Check published package contracts
run: npm run test:package-contract

- name: Bundle isolated package consumers
run: npm run test:consumer

- name: Upload consumer bundle statistics
if: always()
uses: actions/upload-artifact@v4
with:
name: consumer-bundle-stats
path: build/consumer
if-no-files-found: ignore

- name: Check browser test types
run: npx tsc --noEmit -p tests/browser/tsconfig.json

Expand Down
50 changes: 42 additions & 8 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/// <reference types="vite/client" />

import type {Decorator, Preview} from '@storybook/react';
import {ThemeProvider, configure} from '@gravity-ui/uikit';
import React from 'react';
import React, {useEffect, useState} from 'react';
import {settings} from '@gravity-ui/date-utils';
import {setLang} from '../src/i18n';
import EditorWorker from 'monaco-editor/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/languages/features/json/json.worker?worker';

Expand All @@ -18,21 +22,51 @@ self.MonacoEnvironment = {

configure({lang: 'en'});

const withYtComponentsTheme: Decorator = (Story, context) => {
const globalsTheme = context.globals.theme;
const isDark = globalsTheme === 'dark';
const uikitTheme = isDark ? 'dark' : 'light';
function LocalePreview({lang, children}: {lang: 'en' | 'ru'; children: React.ReactNode}) {
const [readyLang, setReadyLang] = useState<string>();
useEffect(() => {
let active = true;
settings.loadLocale(lang).then(() => {
if (!active) return;
settings.setLocale(lang);
configure({lang});
setLang(lang);
setReadyLang(lang);
});
return () => {
active = false;
};
}, [lang]);
return readyLang === lang ? <React.Fragment key={lang}>{children}</React.Fragment> : null;
}

const withYtComponentsTheme: Decorator = (Story, context) => {
const theme = context.globals.theme === 'dark' ? 'dark' : 'light';
const lang = context.globals.locale === 'ru' ? 'ru' : 'en';
return (
<ThemeProvider theme={uikitTheme}>
<Story />
</ThemeProvider>
<LocalePreview lang={lang}>
<ThemeProvider theme={theme}>
<Story />
</ThemeProvider>
</LocalePreview>
);
};

const preview: Preview = {
decorators: [withYtComponentsTheme],
globalTypes: {
locale: {
defaultValue: 'en',
toolbar: {
title: 'Language',
icon: 'globe',
items: [
{value: 'en', title: 'English'},
{value: 'ru', title: 'Русский'},
],
dynamicTitle: true,
},
},
theme: {
defaultValue: 'light',
toolbar: {
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Every component/module/widget/page that needs styles owns an eponymous `.scss` f
- External positioning relative to a parent or siblings belongs to the parent. When a parent needs to apply such styles directly to a child, the child accepts `className` and merges it with its root BEM class; the parent passes its own BEM element, e.g. `className={block('child')}`.
- Prefer `Flex` from `@gravity-ui/uikit` for flexbox layouts when its props can express the required behavior. Use props such as `direction`, `gap`, and `alignItems` instead of duplicating them with `display: flex` in SCSS.
- Prefer container layout such as `Flex` with `gap` when it expresses the relationship; do not add a child class solely to duplicate container spacing. Keep flex rules in SCSS only when the `Flex` API cannot express the required selectors or behavior.
- Do not copy the current legacy patterns where `ClustersList.scss` defines `ClusterRow` styles or `NavigationItemsList.scss` defines `NavigationItemRow` styles. These are existing violations, not examples to follow.
- `ClusterRow` and `NavigationItemRow` own and import their styles. Preserve this ownership when composing them in list modules.

For the full ownership rules, examples, and review checklist, see [`plans/styles-rules.md`](plans/styles-rules.md).

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* **QueryEditor:** add query link sharing dialog ([47c454d](https://github.com/gravity-ui/querieskit/commit/47c454daca5d1cfb9fb2001371701240585a53b9))
* **QueryEditor:** add save menu and simplify stories ([3062817](https://github.com/gravity-ui/querieskit/commit/3062817b75c080987726e1dda20788017c02767c))
* **QueryEditor:** integrate attachments in shared side panel ([c9e7c62](https://github.com/gravity-ui/querieskit/commit/c9e7c6298bd773efd435bc31762945f7c79f7f51))
* **QueryEditor:** integrate settings and YTsaurus engine preset ([9c5a040](https://github.com/gravity-ui/querieskit/commit/9c5a0409de1ad51766b50b8572f9e4b448455886))
* **QueryEditor:** integrate settings and built-in engine preset ([9c5a040](https://github.com/gravity-ui/querieskit/commit/9c5a0409de1ad51766b50b8572f9e4b448455886))
* **QuerySettings:** add extensible query settings form ([5d7321d](https://github.com/gravity-ui/querieskit/commit/5d7321db8b34f54a37f1ac18d3a7287ed40aa791))


Expand Down
69 changes: 69 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Migration notes for the library audit fixes

These changes include breaking changes to engine factory names, preset imports, engine language descriptors and internal type paths. Component callback signatures and unrelated package entrypoints remain available. No release has been published as part of this change.

## Query editor engines

Use `createBuiltinQueryEngines` and the `Builtin`-prefixed public types: `BuiltinQueryEngineId`, `BuiltinEngineSettings`, `BuiltinQueryClique`, `BuiltinDiscoveryGroup` and `BuiltinQueryEnginesOptions`. The former branded factory and types have been removed without aliases. Import the preset from `@gravity-ui/querieskit/widgets/QueryEditor/presets/builtin`; both former branded preset subpaths have been removed, and no `modules` preset alias is provided. Within the repository, public types now live in `src/types/builtinQueryEngines.ts`.

`createBuiltinQueryEngines()` now returns the same Monaco language IDs that `createQueryEditorPreset()` supports:

| Engine ID (unchanged) | Previous language | Current language |
| --------------------- | ----------------- | ---------------- |
| `yql` | `yql` | `yql` |
| `chyt` | `clickhouse` | `clickhouse` |
| `spyt` | `s-expression` | `spyt` |
| `ql` | `yql_ansi` | `ytql` |

Update application checks and custom providers that depended on the old SPYT/QL descriptors. The standalone `s-expression` and `yql_ansi` language registrations are retained for applications explicitly using them.

Pass both UI configurations to the editor to enable engine settings and language services:

```tsx
import {createQueryEditorPreset} from '@gravity-ui/querieskit/helpers/queryEditorPreset';
import {createBuiltinQueryEngines} from '@gravity-ui/querieskit/widgets/QueryEditor/presets/builtin';

const engines = createBuiltinQueryEngines({
yql: {versions: ['stable']},
chyt: {cliques: [{alias: 'analytics', state: 'active', health: 'good'}]},
spyt: {discoveryGroups: [{path: '//sys/spark/clusters/demo'}]},
});
const preset = createQueryEditorPreset({listPathChildren, getTableSchema, onReferenceClick});
// Pass engines and preset in QueryEditor props or QueryPage tab.editorProps.
```

Keep these objects stable until their data changes. Applications still own requests, saved settings, query execution and navigation. QueryPage does not install engine or language presets automatically. The descriptors define UI controls; completion data and navigation are supplied through application callbacks. Its FourEngines story demonstrates all four engines with local adapters and independent tab drafts.

## Public types

Public props and consumer models now live in `src/types`. Import them from the package root or the corresponding unit entrypoint instead of implementation files and internal `types.ts` files:

```ts
import type {
AddChartButtonOption,
Attachment,
AttachItem,
DashboardProps,
DashboardRenderItem,
HistoryFilterProps,
NavigationDetailProps,
SimpleFormProps,
} from '@gravity-ui/querieskit';
```

In particular, replace deep imports of `components/Dashboard/types`, `modules/ChartEditor/types`, attachment editor internals and SimpleForm field implementation types. Old internal paths are not a compatibility contract. Unrelated package subpath aliases are retained.

Within this repository, import types directly from their files. Dashboard types are in `src/types/dashboard.ts`, chart editor types in `chartEditor.ts`, attachment data in `attachments.ts`, attachment editing contracts in `attachmentsEditor.ts`, and chart button types in `addChartButton.ts`. Other extracted unit contracts use the corresponding camel-case `*Props.ts` file. HistoryFilter and NavigationDetail use `historyFilter.ts` and `navigationDetail.ts`; navigation resolver contracts use `navigationDetailResolvers.ts`.

History row/search row, group/header and status/duration contracts now also have descriptive exported names such as `HistoryRowProps` and `QueryDurationProps` instead of a local generic `Props` name. Use `import type` for these dependencies.

## Corrected behavior

- HistoryFilter edits a local draft. Initial precedence is field defaults, then `initialValues`, then `values`. Apply sends the complete draft, including untouched fields. Reset restores field defaults plus `initialValues` and calls `onReset`. A substantive external `values` change replaces the draft; an equivalent object does not. Closing the popup keeps edits.
- Dashboard now calls the existing focus/blur callbacks with item content and its current index. Removed items do not emit callbacks.
- NavigationDetail restores a visible enabled tab after its uncontrolled selection disappears; automatic recovery does not emit a user selection callback. With no enabled tabs it renders empty content. Controlled selection remains application-owned.
- Default chart labels are localized. Custom text/options and technical chart IDs are unchanged. Count labels use “Fields: N” / “Elements: N” and their Russian equivalents.

## Storybook

Use the global Language and Theme controls to check EN/RU and light/dark. Switching language synchronizes querieskit, UIKit and date-utils and remounts the story, resetting demo state. Fixture data is no longer exported as stories. QueryShare controls and Actions now reflect the interactive example.
43 changes: 34 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# QueriesKit · [![npm package](https://img.shields.io/npm/v/@gravity-ui/querieskit?logo=npm)](https://www.npmjs.com/package/@gravity-ui/querieskit) [![CI](https://img.shields.io/github/actions/workflow/status/gravity-ui/querieskit/ci.yml?branch=main&label=CI&logo=github)](https://github.com/gravity-ui/querieskit/actions/workflows/ci.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685?logo=storybook)](https://preview.gravity-ui.com/querieskit/)

React component library for building **query pages** — history, tutorials, editors, and related UI. Part of the [Gravity UI](https://gravity-ui.com) design system.
React component library for displaying query data and building **query pages** — history, tutorials, editors, and related UI. Part of the [Gravity UI](https://gravity-ui.com) design system.

Applications supply data through props and handle callbacks. QueriesKit manages presentation and local UI interactions; it does not fetch data or execute queries. Data loading, query execution, persistence and routing belong to the application.

The library is under active development and its public API is not yet stable.
API changes may be released without a major version bump. Check the changelog
Expand Down Expand Up @@ -64,7 +66,16 @@ Use pages for a complete screen layout, widgets for independent feature blocks,

### Individual imports

Pages, widgets, and modules support both root and individual imports:
All public components, modules, widgets, and pages support both root and individual imports.
Prefer individual imports to keep the dependency graph and extracted CSS focused on the units you use:

```tsx
import {QueryStatusIcon} from '@gravity-ui/querieskit/components/QueryStatusIcon';
import {QueriesHistory} from '@gravity-ui/querieskit/modules/QueriesHistory';
import {QueryEditor} from '@gravity-ui/querieskit/widgets/QueryEditor';
```

Root imports remain supported:

```tsx
import {SavedQueries} from '@gravity-ui/querieskit';
Expand All @@ -77,9 +88,8 @@ import {SavedQueries} from '@gravity-ui/querieskit/modules/SavedQueries';
import type {SavedQueriesProps} from '@gravity-ui/querieskit/modules/SavedQueries';
```

Individual entrypoints are available for `QueryPage` under `pages/`, `QueriesHistory`, `SavedQueries`,
`TutorialsHistory`, `QueriesNavigation` under `modules/`, and `QueriesSidebar`,
`QueryEditor`, `QueryExecutionPanel`, `DashboardCharts` under `widgets/`.
Every public unit re-exported by the four level barrels has an individual entrypoint
under its corresponding level (`components/`, `modules/`, `widgets/`, or `pages/`).
They expose each unit's existing public exports, including its props and helpers.
Shared data types remain available from the package root.

Expand All @@ -88,9 +98,23 @@ Both forms support tree-shaking with an ESM-aware bundler; individual imports do
not necessarily produce a smaller bundle. Existing root imports remain supported.
Keep CSS processing enabled so that the selected widget's styles are included.
Some bundlers, including esbuild, retain CSS from unused root re-exports even when
their JavaScript is removed. Individual widget imports avoid introducing those
their JavaScript is removed. Individual unit imports avoid introducing those
unrelated styles.

For browser builds, preserve ESM imports through to the bundler. Requiring the
package root as CommonJS eagerly traverses its re-exports and can retain unrelated
widgets and dependencies. Individual CJS entrypoints reduce that scope, but the
published CJS build does not preserve the ESM code-splitting boundaries.

Search previews in `QueriesHistory`, `SavedQueries`, and `TutorialsHistory` load Monaco
on demand. While the editor chunk loads, the row displays the query as plain text
without changing its height or link. Configure production code splitting to retain
this loading boundary; a single-file bundle still contains the editor. Chunk-loading
errors propagate to the application's error boundary.

See [the next-major migration notes](docs/migrations/next-major.md) for the explicit
Monaco language registrations and how to connect additional languages.

### QueryPage

Composes a shared `QueriesSidebar` with `QueryTabs`. Each query tab owns an independent
Expand Down Expand Up @@ -146,9 +170,10 @@ Extensions stories. Individual imports are available from `components/MonacoEdit
import {QueryEditor} from '@gravity-ui/querieskit/widgets/QueryEditor';
```

The YTsaurus preset is available from `widgets/QueryEditor/presets/ytsaurus`.
The previous package subpaths `modules/QueryEditor` and
`modules/QueryEditor/presets/ytsaurus` remain supported as compatibility aliases.
Optional engine UI configurations are available from `widgets/QueryEditor/presets/builtin`.
They define language selection, labels and settings controls using application-supplied data.
The previous package subpath `modules/QueryEditor` remains a compatibility alias for the widget.
The built-in preset is available only under the widget subpath.

### QueryExecutionPanel

Expand Down
4 changes: 4 additions & 0 deletions docs/editor-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ container. Existing use without these props keeps its existing behavior.

## Language preset and application data

QueriesKit calls the supplied completion and reference callbacks. The application
provides the data (from local state or its own loading logic) and implements navigation.
The library itself does not fetch catalog data or execute queries.

```tsx
import {QueryEditor} from '@gravity-ui/querieskit';
import {createQueryEditorPreset} from '@gravity-ui/querieskit/helpers/queryEditorPreset';
Expand Down
58 changes: 58 additions & 0 deletions docs/migrations/next-major.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Next major: explicit Monaco languages and isolated imports

This change is intended for the next major release. Component props, root exports,
and existing individual entrypoint aliases remain available. Automatic registration
of additional Monaco languages changes and may require application changes.

## Languages

QueriesKit registers its existing YQL (`yql`), YTQL (`yql_ansi`), CHYT (`clickhouse`),
SPYT (`s-expression`), and JSON (`json`) languages. It no longer imports Monaco's
root entrypoint, which implicitly registered unrelated languages and language services.
The editor's existing commands and contributions remain registered.

Applications using other languages must register them before rendering the editor.
For example, with Monaco 0.56:

```tsx
import 'monaco-editor/languages/definitions/python/register';
import {MonacoEditor} from '@gravity-ui/querieskit/components/MonacoEditor';

<MonacoEditor language="python" value="print('hello')" />;
```

Use the registration entrypoint for the desired language in your installed Monaco
version. Language services may also require their feature registrations and workers.
Keep the application and QueriesKit on the same resolved Monaco version so the
registrations reach the editor instance. Avoid importing Monaco's root merely to
access `editor` or `languages`; use `monaco-editor/editor/editor.api` at runtime.
Type-only imports from `monaco-editor` remain safe.

## Imports and loading

Every public component, module, widget, and page now has an explicit individual
entrypoint with ESM/CJS and TypeScript declarations. Prefer these entrypoints over
`build/...` paths or the root when bundle isolation matters:

```tsx
import {QueryStatusIcon} from '@gravity-ui/querieskit/components/QueryStatusIcon';
import {QueriesHistory} from '@gravity-ui/querieskit/modules/QueriesHistory';
import {QueryEditor} from '@gravity-ui/querieskit/widgets/QueryEditor';
```

Root imports still support JavaScript tree-shaking, but esbuild may extract CSS
from unused root re-exports. This release does not promise CSS isolation for root
imports. CSS remains side-effectful and must be processed by the application.

History search previews now load the editor asynchronously when rendered. They
show plain query text at the same height while loading. Production code splitting
must remain enabled to keep Monaco out of the initial history chunk. A rejected
chunk load reaches the application's error boundary.

## Runtime dependencies and browsers

The package explicitly declares the Babel runtime 7 and core-js versions needed
by its generated JavaScript. An application may use Babel runtime 8 independently;
it must not force QueriesKit's runtime dependency to version 8 through overrides.
Browser targets and automatic polyfills are unchanged. Reducing polyfills is deferred
until supported browsers are agreed with consuming applications.
Loading
Loading