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
3 changes: 3 additions & 0 deletions .agents/skills/code-review-checklist/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ Do not report style-only nits. Order findings from highest to lowest severity.

## Response Format

Attach a rationale to every conclusion, including findings, existing-comment statuses, and a conclusion that no issues were found. Cite concrete evidence (file and line references, relevant contracts, or check results) and explain how it supports the conclusion. Distinguish verified facts from assumptions; for a no-findings conclusion, state the reviewed scope and verification limits rather than implying proof of correctness.

Lead with confirmed findings. For each finding include:

- **Location:** `path/to/file:line`
- **Severity:** `High`, `Medium`, or `Low`
- **Finding:** concise description of the defect
- **Condition:** input or execution path that triggers it
- **Impact:** observable consequence
- **Rationale:** concrete evidence and the causal link between the triggering condition and the impact
- **Recommendation:** specific fix or mitigation

For existing PR comments, also include **Status** using the vocabulary required by `github-pr-review`.
Expand Down
3 changes: 3 additions & 0 deletions .agents/skills/github-pr-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ If the PR already contains comments from other reviewers:
1. **Relevance:** Determine whether each comment still applies. Mark it if the issue has already been fixed in newer commits.
2. **Validity:** Check each comment against the current checklist. Point out any comment that conflicts with the project's standards or the checklist.

Attach a rationale to every conclusion using the checklist's evidence requirements. For each comment status, cite the current code, relevant change, or project rule that justifies marking it Confirmed, Fixed, or Outdated; the status alone is not an explanation.

## Response Format

Use the following format for every finding, whether new or an existing PR comment:
Expand All @@ -27,4 +29,5 @@ Use the following format for every finding, whether new or an existing PR commen
- **Status:** (New / Confirmed / Fixed / Outdated)
- **Severity:** (High / Medium / Low)
- **Issue:** A concise description of the problem based on the checklist.
- **Rationale:** Concrete evidence with file and line references, relevant contracts, or check results, explaining why the issue and its status follow from that evidence.
- **Recommendation:** A concrete example of the corrected code.
23 changes: 23 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

Instructions for agents on the code structure of `@gravity-ui/querieskit`.

## Multi-agent workflow

- Proactively use subagents when independent tasks can run in parallel
and materially improve speed or review quality. No separate user
request is needed.
- Use up to 3 subagents concurrently across the entire task.
Choose the number based on useful independent work.
- Keep small, straightforward changes and tightly coupled work
in the main agent.
- Give each subagent a bounded task, relevant context, expected output,
and explicit permission to edit or instructions to remain read-only.
- For parallel implementation, assign non-overlapping file ownership.
Agree on shared props and public types before splitting dependent work.
Keep shared barrels and integration changes with the main agent.
- Useful delegated tasks in this repository include:
- Checking API levels, import direction, public types, and barrel usage.
- Reviewing style ownership, Gravity UI usage, and i18n conventions.
- Investigating test failures or checking affected Storybook scenarios.
- The main agent should continue complementary work while subagents run.
Avoid duplicating their tasks.
- Before finishing, inspect delegated changes, reconcile findings,
and run the appropriate checks on the integrated result.

## Three levels of the public API

The library is organized into three levels of abstraction. Every new and existing widget must follow this structure:
Expand Down
19 changes: 16 additions & 3 deletions src/components/EmptyContent/EmptyContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import {Folder, NoSearchResults} from '@gravity-ui/illustrations';
import cn from 'bem-cn-lite';
import i18n from './i18n';
import './EmptyContent.scss';
import type {EmptyContentTextOverrides} from '../../types/listPanel';

const block = cn('qp-empty-content');

export type EmptyContentVariant = 'no-files' | 'no-clusters' | 'nothing-found' | 'no-data';

export type EmptyContentProps = {
export type EmptyContentProps = EmptyContentTextOverrides & {
variant: EmptyContentVariant;
className?: string;
};
Expand Down Expand Up @@ -40,8 +41,20 @@ const CONTENT_BY_VARIANT: Record<EmptyContentVariant, EmptyContentConfig> = {
},
};

export const EmptyContent: FC<EmptyContentProps> = ({variant, className}) => {
const {icon: Icon, title, description} = CONTENT_BY_VARIANT[variant];
export const EmptyContent: FC<EmptyContentProps> = ({
variant,
className,
title: titleOverride,
description: descriptionOverride,
}) => {
const {
icon: Icon,
title: defaultTitle,
description: defaultDescription,
} = CONTENT_BY_VARIANT[variant];
const title = titleOverride === undefined ? defaultTitle : titleOverride;
const description =
descriptionOverride === undefined ? defaultDescription : descriptionOverride;

return (
<Flex alignItems="center" justifyContent="center" className={block(null, className)}>
Expand Down
11 changes: 8 additions & 3 deletions src/components/HistoryFilter/HistoryFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,37 @@ import {Button, Flex, Icon, Popup} from '@gravity-ui/uikit';
import FunnelIcon from '@gravity-ui/icons/svgs/funnel.svg';
import ArrowRotateLeftIcon from '@gravity-ui/icons/svgs/arrow-rotate-left.svg';
import {useToggle} from '../../helpers/useToggle';
import {QueryListFilterConfig} from '../../types/queryList';
import type {QueryListFilterConfig} from '../../types/queryList';
import cn from 'bem-cn-lite';
import {SimpleForm} from '../SimpleForm';
import i18n from './i18n';
import './HistoryFilter.scss';

const block = cn('qp-history-filter');

export const HistoryFilter: FC<QueryListFilterConfig> = ({
export const HistoryFilter: FC<QueryListFilterConfig & {buttonView?: 'normal' | 'flat'}> = ({
fields = [],
values,
initialValues,
isChanged,
onApply,
onReset,
buttonView = 'normal',
}) => {
const [buttonElement, setButtonElement] = useState<HTMLButtonElement | null>(null);
const [open, toggleOpen] = useToggle(false);
const valuesRef = useRef<Record<string, any>>(values ?? initialValues ?? {});
const normalView = isChanged ? 'action' : 'normal';
const flatView = isChanged ? 'flat-info' : 'flat';

return (
<>
<Button
ref={setButtonElement}
onClick={toggleOpen}
view={isChanged ? 'action' : 'normal'}
view={buttonView === 'flat' ? flatView : normalView}
selected={buttonView === 'flat' && Boolean(isChanged)}
className={block('button')}
>
<Icon data={FunnelIcon} size={16} />
</Button>
Expand Down
11 changes: 8 additions & 3 deletions src/components/NavigationItemRow/NavigationItemRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ export type NavigationItemRowProps = {
export const NavigationItemRow: FC<NavigationItemRowProps> = ({item}) => {
return (
<Flex gap={2} alignItems="center" className={block({disabled: item.disabled})}>
{item.icon ?? (
<Icon data={getDefaultNavigationIcon(item.kind, item.targetPathBroken)} size={16} />
)}
<Flex width={20} height={20} shrink={0} alignItems="center" justifyContent="center">
{item.icon ?? (
<Icon
data={getDefaultNavigationIcon(item.kind, item.targetPathBroken)}
size={16}
/>
)}
</Flex>
<Text
className={block('title')}
color={item.disabled ? 'secondary' : undefined}
Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchWithButtons/SearchWithButtons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
}

&__inner-buttons {
margin-right: 4px;
margin-right: var(--qp-search-with-buttons-inner-buttons-margin-right, 4px);
}
}
29 changes: 29 additions & 0 deletions src/helpers/filterNavigationRows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type {NavigationPreviewRow} from '../types/navigation';

const stringifyCell = (value: unknown): string => {
if (value === undefined || value === null) {
return '';
}
if (typeof value === 'string') {
return value;
}
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value);
}
return '';
};

export function filterNavigationRows<TRow extends NavigationPreviewRow>(
rows: TRow[],
columns: string[],
search?: string,
): TRow[] {
const query = search?.trim().toLowerCase();
if (!query) {
return rows;
}

return rows.filter((row) =>
columns.some((column) => stringifyCell(row[column]).toLowerCase().includes(query)),
);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './components';
export * from './modules';
export * from './widgets';
export * from './types/queryList';
export * from './types/listPanel';
export * from './types/history';
export type {SavedQuery} from './types/savedQueries';
export * from './types/tutorial';
Expand Down
6 changes: 6 additions & 0 deletions src/modules/HistoryHeader/HistoryHeader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.qp-history-header {
&_variant_tutorials,
&_variant_panel {
--qp-search-with-buttons-inner-buttons-margin-right: 2px;
}
}
26 changes: 23 additions & 3 deletions src/modules/HistoryHeader/HistoryHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import React, {FC, useEffect, useState} from 'react';
import {FullSearchToggleButton} from './internal/FullSearchToggleButton';
import {HistoryFilter} from '../../components/HistoryFilter';
import {SearchWithButtons} from '../../components/SearchWithButtons';
import {QueryListFilterConfig} from '../../types/queryList';
import type {QueryListFilterConfig} from '../../types/queryList';
import i18n from './i18n';
import cn from 'bem-cn-lite';
import type {HistoryPanelVariant} from '../../types/listPanel';
import './HistoryHeader.scss';

const block = cn('qp-history-header');

type Props = {
search?: string;
Expand All @@ -13,6 +19,7 @@ type Props = {
actions?: React.ReactNode;
onUpdate: (data: {value: string; fullSearch: boolean}) => void;
className?: string;
variant?: HistoryPanelVariant;
};

export const HistoryHeader: FC<Props> = ({
Expand All @@ -24,7 +31,9 @@ export const HistoryHeader: FC<Props> = ({
actions,
onUpdate,
className,
variant = 'default',
}) => {
const isPanel = variant !== 'default';
const [searchValue, setSearchValue] = useState(search || '');
const [isFullSearch, setFullSearch] = useState(fullSearchAvailable && Boolean(fullSearch));

Expand All @@ -46,7 +55,9 @@ export const HistoryHeader: FC<Props> = ({

return (
<SearchWithButtons
className={className}
placeholder={isPanel ? i18n('field_search') : undefined}
gap={isPanel ? 2 : 1}
className={block({variant}, className)}
value={searchValue}
hasClear={hasClear}
onUpdate={handleOnUpdate}
Expand All @@ -55,14 +66,23 @@ export const HistoryHeader: FC<Props> = ({
? [
<FullSearchToggleButton
key="full-search"
appearance={variant === 'default' ? 'default' : 'flat'}
active={isFullSearch}
onClick={handleModeChange}
/>,
]
: undefined
}
endButtons={[
...(filter ? [<HistoryFilter key="filter" {...filter} />] : []),
...(filter
? [
<HistoryFilter
key="filter"
{...filter}
buttonView={isPanel ? 'flat' : 'normal'}
/>,
]
: []),
...(actions ? [<React.Fragment key="actions">{actions}</React.Fragment>] : []),
]}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/modules/HistoryHeader/i18n/dicts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import en from './en.json';
import ru from './ru.json';

export default {en, ru};
4 changes: 4 additions & 0 deletions src/modules/HistoryHeader/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"field_search": "Search",
"action_full-search": "Search in query text"
}
4 changes: 4 additions & 0 deletions src/modules/HistoryHeader/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {addI18Keysets} from '../../../i18n';
import dicts from './dicts';

export default addI18Keysets('qp:history-header', dicts);
4 changes: 4 additions & 0 deletions src/modules/HistoryHeader/i18n/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"field_search": "Поиск",
"action_full-search": "Искать в тексте запроса"
}
20 changes: 17 additions & 3 deletions src/modules/HistoryHeader/internal/FullSearchToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import React, {FC} from 'react';
import {Button, Icon} from '@gravity-ui/uikit';
import CodeIcon from '@gravity-ui/icons/svgs/code.svg';
import ChevronsExpandHorizontalIcon from '@gravity-ui/icons/svgs/chevrons-expand-horizontal.svg';
import i18n from '../i18n';

type Props = {
active?: boolean;
onClick: () => void;
appearance?: 'default' | 'flat';
};

export const FullSearchToggleButton: FC<Props> = ({active, onClick}) => {
export const FullSearchToggleButton: FC<Props> = ({active, onClick, appearance = 'default'}) => {
const defaultView = active ? 'action' : 'normal';
return (
<Button size="xs" view={active ? 'action' : undefined} onClick={onClick}>
<Icon data={ChevronsExpandHorizontalIcon} size={12} />
<Button
size={appearance === 'flat' ? 's' : 'xs'}
view={appearance === 'flat' ? 'flat-secondary' : defaultView}
selected={appearance === 'flat' && active}
aria-pressed={Boolean(active)}
aria-label={i18n('action_full-search')}
onClick={onClick}
>
<Icon
data={appearance === 'flat' ? CodeIcon : ChevronsExpandHorizontalIcon}
size={12}
/>
</Button>
);
};
24 changes: 24 additions & 0 deletions src/modules/HistoryLayout/HistoryLayout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,28 @@
&__header {
padding: 0 12px;
}

&_variant_tutorials,
&_variant_panel {
min-height: 0;

.qp-history-layout__header {
padding: 0;
flex-shrink: 0;
}

.qp-history-layout__service {
min-height: 32px;
padding: 2px 8px 2px 12px;
box-sizing: border-box;
}

.qp-history-layout__title {
padding: 8px 12px 2px;
}

.qp-history-layout__controls {
padding: 4px 8px 4px 12px;
}
}
}
Loading
Loading