ydb-embedded-ui 1.13.2 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/assets/icons/flask.svg +3 -0
  3. package/dist/components/InfoViewer/formatters/common.ts +15 -0
  4. package/dist/components/InfoViewer/formatters/index.ts +2 -0
  5. package/dist/components/InfoViewer/formatters/schema.ts +43 -0
  6. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +44 -0
  7. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +34 -0
  8. package/dist/components/{IndexInfoViewer/IndexInfoViewer.tsx → InfoViewer/schemaInfo/TableIndexInfo.tsx} +7 -18
  9. package/dist/components/InfoViewer/schemaInfo/index.ts +3 -0
  10. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +44 -0
  11. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +35 -0
  12. package/dist/components/InfoViewer/schemaOverview/index.ts +2 -0
  13. package/dist/components/QueryResultTable/Cell/Cell.tsx +33 -0
  14. package/dist/components/QueryResultTable/Cell/index.ts +1 -0
  15. package/dist/components/QueryResultTable/QueryResultTable.scss +11 -0
  16. package/dist/components/QueryResultTable/QueryResultTable.tsx +115 -0
  17. package/dist/components/QueryResultTable/i18n/en.json +3 -0
  18. package/dist/components/QueryResultTable/i18n/index.ts +11 -0
  19. package/dist/components/QueryResultTable/i18n/ru.json +3 -0
  20. package/dist/components/QueryResultTable/index.ts +1 -0
  21. package/dist/containers/App/App.scss +1 -0
  22. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +39 -14
  23. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +18 -7
  24. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +4 -3
  25. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +7 -7
  26. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -2
  27. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  28. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +8 -3
  29. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -1
  30. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +1 -1
  31. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +36 -10
  32. package/dist/containers/Tenant/Preview/Preview.js +15 -57
  33. package/dist/containers/Tenant/Preview/Preview.scss +4 -8
  34. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -41
  35. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -4
  36. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.scss +1 -2
  37. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  38. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -1
  39. package/dist/containers/Tenant/utils/schema.ts +3 -0
  40. package/dist/containers/Tenant/utils/schemaActions.ts +1 -2
  41. package/dist/containers/Tenants/Tenants.js +12 -2
  42. package/dist/containers/UserSettings/UserSettings.tsx +26 -3
  43. package/dist/services/api.d.ts +19 -2
  44. package/dist/services/api.js +2 -2
  45. package/dist/setupTests.js +4 -0
  46. package/dist/store/reducers/executeQuery.js +4 -9
  47. package/dist/store/reducers/{preview.js → preview.ts} +22 -18
  48. package/dist/store/reducers/settings.js +3 -1
  49. package/dist/store/utils.ts +88 -0
  50. package/dist/types/api/query.ts +147 -0
  51. package/dist/types/api/schema.ts +235 -2
  52. package/dist/types/index.ts +33 -0
  53. package/dist/types/store/query.ts +9 -0
  54. package/dist/utils/{constants.js → constants.ts} +11 -6
  55. package/dist/utils/index.js +0 -24
  56. package/dist/utils/query.test.ts +189 -0
  57. package/dist/utils/query.ts +156 -0
  58. package/dist/utils/tests/providers.tsx +29 -0
  59. package/package.json +2 -2
  60. package/dist/store/utils.js +0 -51
@@ -1,7 +1,12 @@
1
1
  import React from 'react';
2
- import InternalLink from '../../../components/InternalLink/InternalLink';
2
+ import {useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
+ import {INVERTED_DISKS_KEY} from '../../../utils/constants';
6
+ import {getSettingValue} from '../../../store/reducers/settings';
7
+
8
+ import InternalLink from '../../../components/InternalLink/InternalLink';
9
+
5
10
  import './DiskStateProgressBar.scss';
6
11
 
7
12
  const b = cn('storage-disk-progress-bar');
@@ -26,11 +31,16 @@ function DiskStateProgressBar({
26
31
  severity,
27
32
  href,
28
33
  }: DiskStateProgressBarProps) {
34
+ const inverted = useSelector((state) => getSettingValue(state, INVERTED_DISKS_KEY));
35
+
29
36
  const renderAllocatedPercent = () => {
30
37
  return (
31
38
  diskAllocatedPercent >= 0 && (
32
39
  <React.Fragment>
33
- <div className={b('filled')} style={{width: `${diskAllocatedPercent}%`}} />
40
+ <div
41
+ className={b('filled')}
42
+ style={{width: `${inverted ? 100 - diskAllocatedPercent : diskAllocatedPercent}%`}}
43
+ />
34
44
  <div className={b('filled-title')}>
35
45
  {`${Math.round(diskAllocatedPercent)}%`}
36
46
  </div>
@@ -39,13 +49,14 @@ function DiskStateProgressBar({
39
49
  );
40
50
  };
41
51
 
52
+ const mods: Record<string, boolean | undefined> = {inverted};
53
+ if (severity !== undefined && severity in diskProgressColors) {
54
+ mods[diskProgressColors[severity].toLocaleLowerCase()] = true;
55
+ }
56
+
42
57
  return (
43
58
  <div
44
- className={
45
- severity !== undefined
46
- ? b({[diskProgressColors[severity].toLowerCase()]: true})
47
- : undefined
48
- }
59
+ className={b(mods)}
49
60
  role="meter"
50
61
  aria-label="Disk allocated space"
51
62
  aria-valuemin={0}
@@ -1,13 +1,14 @@
1
- import {render} from '@testing-library/react'
2
1
  import {MemoryRouter} from 'react-router-dom';
3
2
 
3
+ import {renderWithStore} from '../../../../utils/tests/providers';
4
+
4
5
  import {TPDiskState} from '../../../../types/api/storage'
5
6
 
6
7
  import PDisk from '../Pdisk'
7
8
 
8
9
  describe('PDisk state', () => {
9
10
  it('Should determine severity based on State', () => {
10
- const {getAllByRole} = render(
11
+ const {getAllByRole} = renderWithStore(
11
12
  <MemoryRouter>
12
13
  <PDisk
13
14
  NodeId={1}
@@ -26,7 +27,7 @@ describe('PDisk state', () => {
26
27
  });
27
28
 
28
29
  it('Should display as unavailabe when no State is provided', () => {
29
- const {getByRole} = render(
30
+ const {getByRole} = renderWithStore(
30
31
  <MemoryRouter>
31
32
  <PDisk NodeId={1} />
32
33
  </MemoryRouter>
@@ -1,10 +1,10 @@
1
- import {render} from '@testing-library/react'
1
+ import {renderWithStore} from '../../../../utils/tests/providers';
2
2
 
3
3
  import VDisk from '../Vdisk'
4
4
 
5
5
  describe('VDisk state', () => {
6
6
  it('Should determine severity based on the highest value among VDiskState, DiskSpace and FrontQueues', () => {
7
- const {getAllByRole} = render(
7
+ const {getAllByRole} = renderWithStore(
8
8
  <>
9
9
  <VDisk
10
10
  VDiskId={{Domain: 1}}
@@ -35,7 +35,7 @@ describe('VDisk state', () => {
35
35
  });
36
36
 
37
37
  it('Should not pick the highest severity based on FrontQueues value', () => {
38
- const {getAllByRole} = render(
38
+ const {getAllByRole} = renderWithStore(
39
39
  <>
40
40
  <VDisk
41
41
  VDiskId={{Domain: 1}}
@@ -59,7 +59,7 @@ describe('VDisk state', () => {
59
59
  });
60
60
 
61
61
  it('Should display as unavailable when no VDiskState is provided', () => {
62
- const {getAllByRole} = render(
62
+ const {getAllByRole} = renderWithStore(
63
63
  <>
64
64
  <VDisk VDiskId={{Domain: 1}} />
65
65
  <VDisk VDiskId={{Domain: 2}} VDiskState="OK" />
@@ -86,7 +86,7 @@ describe('VDisk state', () => {
86
86
  });
87
87
 
88
88
  it('Should display replicating VDisks in OK state with a distinct color', () => {
89
- const {getAllByRole} = render(
89
+ const {getAllByRole} = renderWithStore(
90
90
  <>
91
91
  <VDisk
92
92
  VDiskId={{Domain: 1}}
@@ -108,7 +108,7 @@ describe('VDisk state', () => {
108
108
  });
109
109
 
110
110
  it('Should display replicating VDisks in a not-OK state with a regular color', () => {
111
- const {getAllByRole} = render(
111
+ const {getAllByRole} = renderWithStore(
112
112
  <>
113
113
  <VDisk
114
114
  VDiskId={{Domain: 1}}
@@ -130,7 +130,7 @@ describe('VDisk state', () => {
130
130
  });
131
131
 
132
132
  it('Should always display donor VDisks with a regular color', () => {
133
- const {getAllByRole} = render(
133
+ const {getAllByRole} = renderWithStore(
134
134
  <>
135
135
  <VDisk
136
136
  VDiskId={{Domain: 1}}
@@ -20,7 +20,7 @@ type Page = {
20
20
 
21
21
  const overview = {
22
22
  id: GeneralPagesIds.overview,
23
- title: 'Overview',
23
+ title: 'Info',
24
24
  };
25
25
 
26
26
  const topQueries = {
@@ -81,6 +81,8 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
81
81
 
82
82
  export const DIR_PAGES = [overview, topShards, describe];
83
83
 
84
+ export const TOPIC_PAGES = [overview, describe];
85
+
84
86
  // verbose mapping to guarantee correct tabs for new path types
85
87
  // TS will error when a new type is added but not mapped here
86
88
  const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
@@ -95,7 +97,9 @@ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
95
97
 
96
98
  [EPathType.EPathTypeDir]: DIR_PAGES,
97
99
  [EPathType.EPathTypeTableIndex]: DIR_PAGES,
98
- [EPathType.EPathTypeCdcStream]: DIR_PAGES,
100
+
101
+ [EPathType.EPathTypeCdcStream]: TOPIC_PAGES,
102
+ [EPathType.EPathTypePersQueueGroup]: TOPIC_PAGES,
99
103
  };
100
104
 
101
105
  export const getPagesByType = (type?: EPathType) =>
@@ -8,7 +8,7 @@ import Icon from '../../../../components/Icon/Icon';
8
8
 
9
9
  import {AutoFetcher} from '../../../../utils/autofetcher';
10
10
  import {getHotKeys, setHotKeysOptions} from '../../../../store/reducers/hotKeys';
11
- import {prepareQueryError} from '../../../../utils';
11
+ import {prepareQueryError} from '../../../../utils/query';
12
12
 
13
13
  import {isColumnEntityType, isTableType} from '../../utils/schema';
14
14
 
@@ -6,7 +6,11 @@ import {Loader} from '@yandex-cloud/uikit';
6
6
 
7
7
  //@ts-ignore
8
8
  import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
- import {IndexInfoViewer} from '../../../../components/IndexInfoViewer/IndexInfoViewer';
9
+ import {
10
+ CDCStreamInfo,
11
+ TableIndexInfo,
12
+ PersQueueGroupInfo,
13
+ } from '../../../../components/InfoViewer/schemaInfo';
10
14
 
11
15
  import {EPathType} from '../../../../types/api/schema';
12
16
  import {isColumnEntityType, isTableType} from '../../utils/schema';
@@ -121,11 +125,12 @@ function Overview(props: OverviewProps) {
121
125
  [EPathType.EPathTypeDir]: undefined,
122
126
  [EPathType.EPathTypeTable]: undefined,
123
127
  [EPathType.EPathTypeSubDomain]: undefined,
124
- [EPathType.EPathTypeTableIndex]: () => <IndexInfoViewer data={schemaData} />,
128
+ [EPathType.EPathTypeTableIndex]: () => <TableIndexInfo data={schemaData} />,
125
129
  [EPathType.EPathTypeExtSubDomain]: undefined,
126
130
  [EPathType.EPathTypeColumnStore]: undefined,
127
131
  [EPathType.EPathTypeColumnTable]: undefined,
128
- [EPathType.EPathTypeCdcStream]: undefined,
132
+ [EPathType.EPathTypeCdcStream]: () => <CDCStreamInfo data={schemaData} />,
133
+ [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
129
134
  };
130
135
 
131
136
  return (props.type && pathTypeToComponent[props.type]?.()) || (
@@ -13,7 +13,7 @@ import {isColumnEntityType} from '../../utils/schema';
13
13
 
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {TenantGeneralTabsIds} from '../../TenantPages';
16
- import {prepareQueryError} from '../../../../utils';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
17
 
18
18
  import './TopQueries.scss';
19
19
 
@@ -13,7 +13,7 @@ import {AutoFetcher} from '../../../../utils/autofetcher';
13
13
  import HistoryContext from '../../../../contexts/HistoryContext';
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {isColumnEntityType} from '../../utils/schema';
16
- import {prepareQueryError} from '../../../../utils';
16
+ import {prepareQueryError} from '../../../../utils/query';
17
17
  import {i18n} from '../../../../utils/i18n';
18
18
 
19
19
  import './TopShards.scss';
@@ -1,4 +1,4 @@
1
- import React, {useEffect, useReducer} from 'react';
1
+ import React, {ReactNode, useEffect, useReducer} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import {Link} from 'react-router-dom';
4
4
  import cn from 'bem-cn-lite';
@@ -14,9 +14,13 @@ import Acl from '../Acl/Acl';
14
14
  import SchemaViewer from '../Schema/SchemaViewer/SchemaViewer';
15
15
  import CopyToClipboard from '../../../components/CopyToClipboard/CopyToClipboard';
16
16
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
17
+ import {
18
+ CDCStreamOverview,
19
+ PersQueueGroupOverview,
20
+ } from '../../../components/InfoViewer/schemaOverview';
17
21
  import Icon from '../../../components/Icon/Icon';
18
22
 
19
- import type {EPathSubType, EPathType} from '../../../types/api/schema';
23
+ import {EPathSubType, EPathType, TDirEntry} from '../../../types/api/schema';
20
24
  import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
21
25
 
22
26
  import {
@@ -100,8 +104,8 @@ function ObjectSummary(props: ObjectSummaryProps) {
100
104
  });
101
105
 
102
106
  const {name: tenantName, info: infoTab} = queryParams;
103
- const pathData = _.get(data[tenantName as string], 'PathDescription.Self');
104
- const currentSchemaData = _.get(data[currentSchemaPath], 'PathDescription.Self');
107
+ const pathData: TDirEntry | undefined = _.get(data[tenantName as string], 'PathDescription.Self');
108
+ const currentSchemaData: TDirEntry | undefined = _.get(data[currentSchemaPath], 'PathDescription.Self');
105
109
 
106
110
  const tableSchema =
107
111
  currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
@@ -151,14 +155,36 @@ function ObjectSummary(props: ObjectSummaryProps) {
151
155
  };
152
156
 
153
157
  const renderObjectOverview = () => {
154
- const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
155
- let createTime = '';
156
- if (startTimeInMilliseconds) {
157
- createTime = new Date(startTimeInMilliseconds).toUTCString();
158
+ // verbose mapping to guarantee a correct render for new path types
159
+ // TS will error when a new type is added but not mapped here
160
+ const pathTypeToComponent: Record<EPathType, (() => ReactNode) | undefined> = {
161
+ [EPathType.EPathTypeInvalid]: undefined,
162
+ [EPathType.EPathTypeDir]: undefined,
163
+ [EPathType.EPathTypeTable]: undefined,
164
+ [EPathType.EPathTypeSubDomain]: undefined,
165
+ [EPathType.EPathTypeTableIndex]: undefined,
166
+ [EPathType.EPathTypeExtSubDomain]: undefined,
167
+ [EPathType.EPathTypeColumnStore]: undefined,
168
+ [EPathType.EPathTypeColumnTable]: undefined,
169
+ [EPathType.EPathTypeCdcStream]: () => <CDCStreamOverview data={data[currentSchemaPath]} />,
170
+ [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupOverview data={data[currentSchemaPath]} />,
171
+ };
172
+
173
+ let component = currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.();
174
+
175
+ if (!component) {
176
+ const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
177
+ let createTime = '';
178
+ if (startTimeInMilliseconds) {
179
+ createTime = new Date(startTimeInMilliseconds).toUTCString();
180
+ }
181
+
182
+ component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
158
183
  }
184
+
159
185
  return (
160
186
  <div className={b('overview-wrapper')}>
161
- <InfoViewer info={[{label: 'Create time', value: createTime}]} />
187
+ {component}
162
188
  </div>
163
189
  );
164
190
  };
@@ -203,7 +229,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
203
229
  rootPath={tenantName as string}
204
230
  // for the root pathData.Name contains the same string as tenantName,
205
231
  // but without the leading slash
206
- rootName={pathData?.Name || tenantName}
232
+ rootName={pathData.Name || String(tenantName)}
207
233
  rootType={pathData.PathType}
208
234
  currentPath={currentSchemaPath}
209
235
  />
@@ -3,30 +3,22 @@ import PropTypes from 'prop-types';
3
3
  import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
5
 
6
- import DataTable from '@yandex-cloud/react-data-table';
7
6
  import {Loader, Button} from '@yandex-cloud/uikit';
8
7
 
9
8
  import Icon from '../../../components/Icon/Icon';
10
9
  import Fullscreen from '../../../components/Fullscreen/Fullscreen';
10
+ import {QueryResultTable} from '../../../components/QueryResultTable';
11
11
 
12
12
  import {sendQuery, setQueryOptions} from '../../../store/reducers/preview';
13
- import {showTooltip, hideTooltip} from '../../../store/reducers/tooltip';
14
- import {prepareQueryError, prepareQueryResponse} from '../../../utils/index';
15
- import {isNumeric} from '../../../utils/utils';
13
+ import {prepareQueryError} from '../../../utils/query';
16
14
 
17
15
  import {isTableType} from '../utils/schema';
18
16
  import {AutoFetcher} from '../../../utils/autofetcher';
19
17
  import EnableFullscreenButton from '../../../components/EnableFullscreenButton/EnableFullscreenButton';
20
- import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
21
18
  import {setShowPreview} from '../../../store/reducers/schema';
22
19
 
23
20
  import './Preview.scss';
24
21
 
25
- const TABLE_SETTINGS = {
26
- ...DEFAULT_TABLE_SETTINGS,
27
- stripedRows: true,
28
- };
29
-
30
22
  const b = cn('kv-preview');
31
23
 
32
24
  class Preview extends React.Component {
@@ -38,8 +30,6 @@ class Preview extends React.Component {
38
30
  data: PropTypes.array,
39
31
  loading: PropTypes.bool,
40
32
  type: PropTypes.string,
41
- showTooltip: PropTypes.func,
42
- hideTooltip: PropTypes.func,
43
33
  partCount: PropTypes.string,
44
34
  };
45
35
 
@@ -55,11 +45,10 @@ class Preview extends React.Component {
55
45
  }
56
46
 
57
47
  componentDidUpdate(prevProps) {
58
- const {table, hideTooltip, autorefresh, setQueryOptions} = this.props;
48
+ const {table, autorefresh, setQueryOptions} = this.props;
59
49
 
60
50
  if (prevProps.table !== table) {
61
51
  this.sendQueryForPreview();
62
- hideTooltip();
63
52
  setQueryOptions({
64
53
  wasLoaded: false,
65
54
  data: undefined,
@@ -121,41 +110,8 @@ class Preview extends React.Component {
121
110
  );
122
111
  };
123
112
 
124
- renderTable = () => {
125
- const {data, showTooltip} = this.props;
126
-
127
- let columns = [];
128
- if (data && data.length > 0) {
129
- columns = Object.keys(data[0]).map((key) => ({
130
- name: key,
131
- align: isNumeric(data[0][key]) ? DataTable.RIGHT : DataTable.LEFT,
132
- sortAccessor: (row) => isNumeric(row[key]) ? Number(row[key]) : row[key],
133
- render: ({value}) => {
134
- return (
135
- <span
136
- className={b('cell')}
137
- onClick={(e) => showTooltip(e.target, value, 'cell')}
138
- >
139
- {value}
140
- </span>
141
- );
142
- },
143
- }));
144
- }
145
-
146
- const preparedData = prepareQueryResponse(data);
147
-
148
- return <DataTable columns={columns} data={preparedData} settings={TABLE_SETTINGS} />;
149
- };
150
-
151
113
  render() {
152
- const {error, loading, data = [], type, wasLoaded, isFullscreen} = this.props;
153
-
154
- let message;
155
-
156
- if (!isTableType(type)) {
157
- message = <div className={b('message-container')}>Not available</div>;
158
- }
114
+ const {error, loading, data, type, wasLoaded, isFullscreen} = this.props;
159
115
 
160
116
  if (loading && !wasLoaded) {
161
117
  return (
@@ -165,15 +121,19 @@ class Preview extends React.Component {
165
121
  );
166
122
  }
167
123
 
168
- if (error) {
169
- message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
170
- }
124
+ let message;
171
125
 
172
- if (!loading && data.length === 0) {
173
- message = <div className={b('message-container')}>Table is empty</div>;
126
+ if (!isTableType(type)) {
127
+ message = <div className={b('message-container')}>Not available</div>;
128
+ } else if (error) {
129
+ message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
174
130
  }
175
131
 
176
- const content = message ?? <div className={b('result')}>{this.renderTable()}</div>;
132
+ const content = message ?? (
133
+ <div className={b('result')}>
134
+ <QueryResultTable data={data.result} columns={data.columns} />
135
+ </div>
136
+ );
177
137
 
178
138
  return (
179
139
  <div className={b()}>
@@ -185,7 +145,7 @@ class Preview extends React.Component {
185
145
  }
186
146
 
187
147
  const mapStateToProps = (state) => {
188
- const {data = [], loading, error, wasLoaded} = state.preview;
148
+ const {data = {}, loading, error, wasLoaded} = state.preview;
189
149
  const {autorefresh, currentSchemaPath} = state.schema;
190
150
 
191
151
  return {
@@ -201,8 +161,6 @@ const mapStateToProps = (state) => {
201
161
 
202
162
  const mapDispatchToProps = {
203
163
  sendQuery,
204
- showTooltip,
205
- hideTooltip,
206
164
  setQueryOptions,
207
165
  setShowPreview,
208
166
  };
@@ -16,8 +16,8 @@
16
16
  justify-content: space-between;
17
17
  align-items: center;
18
18
 
19
- height: 40px;
20
- padding: 0 19px;
19
+ height: 53px;
20
+ padding: 0 20px;
21
21
 
22
22
  border-bottom: 1px solid var(--yc-color-line-generic);
23
23
  background-color: var(--yc-color-base-background);
@@ -38,7 +38,7 @@
38
38
  gap: 5px;
39
39
  }
40
40
  &__message-container {
41
- padding: 15px 19px;
41
+ padding: 15px 20px;
42
42
  }
43
43
 
44
44
  &__loader-container {
@@ -49,14 +49,10 @@
49
49
  height: 100%;
50
50
  }
51
51
 
52
- &__cell {
53
- @include cell-container;
54
- }
55
-
56
52
  &__result {
57
53
  overflow: auto;
58
54
 
59
55
  height: calc(100% - 40px);
60
- padding: 0 19px;
56
+ padding: 0 10px;
61
57
  }
62
58
  }
@@ -4,9 +4,10 @@ import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
5
  import _ from 'lodash';
6
6
  import MonacoEditor from 'react-monaco-editor';
7
- import DataTable from '@yandex-cloud/react-data-table';
8
7
  import {Button, DropdownMenu} from '@yandex-cloud/uikit';
8
+
9
9
  import SplitPane from '../../../components/SplitPane';
10
+ import {QueryResultTable} from '../../../components/QueryResultTable';
10
11
 
11
12
  import SaveQuery from './SaveQuery/SaveQuery';
12
13
  import SavedQueries from './SavedQueries/SavedQueries';
@@ -26,16 +27,13 @@ import {
26
27
  setMonacoHotKey,
27
28
  } from '../../../store/reducers/executeQuery';
28
29
  import {getExplainQuery, getExplainQueryAst} from '../../../store/reducers/explainQuery';
29
- import {showTooltip} from '../../../store/reducers/tooltip';
30
30
  import {getSettingValue, setSettingValue} from '../../../store/reducers/settings';
31
31
  import {
32
32
  DEFAULT_IS_QUERY_RESULT_COLLAPSED,
33
33
  DEFAULT_SIZE_RESULT_PANE_KEY,
34
- DEFAULT_TABLE_SETTINGS,
35
34
  SAVED_QUERIES_KEY,
36
35
  QUERY_INITIAL_RUN_ACTION_KEY,
37
36
  } from '../../../utils/constants';
38
- import {prepareQueryResponse} from '../../../utils/index';
39
37
 
40
38
  import {parseJson} from '../../../utils/utils';
41
39
 
@@ -55,11 +53,9 @@ export const RUN_ACTIONS = [
55
53
  ];
56
54
 
57
55
  const TABLE_SETTINGS = {
58
- ...DEFAULT_TABLE_SETTINGS,
59
56
  sortable: false,
60
57
  dynamicItemSizeGetter: () => 40,
61
58
  dynamicRenderType: 'variable',
62
- stripedRows: true,
63
59
  };
64
60
 
65
61
  const EDITOR_OPTIONS = {
@@ -84,7 +80,6 @@ const propTypes = {
84
80
  response: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
85
81
  executeQuery: PropTypes.object,
86
82
  explainQuery: PropTypes.object,
87
- showTooltip: PropTypes.func,
88
83
  setMonacoHotKey: PropTypes.func,
89
84
  theme: PropTypes.string,
90
85
  type: PropTypes.string,
@@ -306,40 +301,17 @@ function QueryEditor(props) {
306
301
  const renderExecuteQuery = () => {
307
302
  const {
308
303
  executeQuery: {data, error, stats},
309
- showTooltip,
310
304
  } = props;
311
305
 
312
306
  let content;
313
307
  if (data) {
314
- let columns = [];
315
- if (data.length > 0) {
316
- columns = Object.keys(data[0]).map((key) => ({
317
- name: key,
318
- render: ({value}) => {
319
- return (
320
- <span
321
- className={b('cell')}
322
- onClick={(e) => showTooltip(e.target, value, 'cell')}
323
- >
324
- {value}
325
- </span>
326
- );
327
- },
328
- }));
329
- const preparedData = prepareQueryResponse(data);
330
-
331
- content = columns.length ? (
332
- <DataTable
333
- columns={columns}
334
- data={preparedData}
335
- settings={TABLE_SETTINGS}
336
- theme="yandex-cloud"
337
- rowKey={(_, index) => index}
338
- />
339
- ) : (
340
- <div>{data}</div>
341
- );
342
- }
308
+ content = (
309
+ <QueryResultTable
310
+ data={data.result}
311
+ columns={data.columns}
312
+ settings={TABLE_SETTINGS}
313
+ />
314
+ );
343
315
  }
344
316
  const textResults = getPreparedResult();
345
317
  const disabled = !textResults.length || resultType !== RESULT_TYPES.EXECUTE;
@@ -469,12 +441,12 @@ function QueryEditor(props) {
469
441
  const columnDivider = '\t';
470
442
  const rowDivider = '\n';
471
443
 
472
- if (!data?.length) {
444
+ if (!data?.result?.length) {
473
445
  return '';
474
446
  }
475
447
 
476
- const columnHeaders = Object.keys(data[0]);
477
- const rows = [columnHeaders].concat(data);
448
+ const columnHeaders = Object.keys(data.result[0]);
449
+ const rows = [columnHeaders].concat(data.result);
478
450
 
479
451
  return rows
480
452
  .map((item) => {
@@ -675,7 +647,6 @@ const mapDispatchToProps = {
675
647
  saveQueryToHistory,
676
648
  goToPreviousQuery,
677
649
  goToNextQuery,
678
- showTooltip,
679
650
  getExplainQuery,
680
651
  getExplainQueryAst,
681
652
  setSettingValue,
@@ -94,10 +94,6 @@
94
94
  color: var(--yc-color-text-secondary);
95
95
  }
96
96
 
97
- &__cell {
98
- @include cell-container;
99
- }
100
-
101
97
  &__select-query-action {
102
98
  margin-left: 2px;
103
99
  }
@@ -7,8 +7,7 @@
7
7
  flex-grow: 1;
8
8
  flex-direction: column;
9
9
 
10
- margin-top: 20px;
11
- padding: 0px 8px;
10
+ padding: 0px 10px;
12
11
  }
13
12
  &__controls {
14
13
  position: sticky;
@@ -7,8 +7,8 @@
7
7
  flex-grow: 1;
8
8
  flex-direction: column;
9
9
 
10
- margin-top: 20px;
11
- padding: 0px 8px;
10
+ padding: 0px 10px;
11
+
12
12
  @include query-data-table();
13
13
  & .data-table__table-wrapper {
14
14
  padding-bottom: 0;
@@ -14,7 +14,7 @@ import {getActions} from '../../utils/schemaActions';
14
14
  interface SchemaTreeProps {
15
15
  rootPath: string;
16
16
  rootName: string;
17
- rootType: EPathType;
17
+ rootType?: EPathType;
18
18
  currentPath: string;
19
19
  }
20
20
 
@@ -29,6 +29,7 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
29
29
  [EPathType.EPathTypeColumnTable]: 'column_table',
30
30
 
31
31
  [EPathType.EPathTypeCdcStream]: 'topic',
32
+ [EPathType.EPathTypePersQueueGroup]: 'topic',
32
33
  };
33
34
 
34
35
  export const mapPathTypeToNavigationTreeType = (
@@ -51,6 +52,7 @@ const pathTypeToIsTable: Record<EPathType, boolean> = {
51
52
  [EPathType.EPathTypeExtSubDomain]: false,
52
53
  [EPathType.EPathTypeColumnStore]: false,
53
54
  [EPathType.EPathTypeCdcStream]: false,
55
+ [EPathType.EPathTypePersQueueGroup]: false,
54
56
  };
55
57
 
56
58
  export const isTableType = (pathType?: EPathType) =>
@@ -82,6 +84,7 @@ const pathTypeToIsColumn: Record<EPathType, boolean> = {
82
84
  [EPathType.EPathTypeTableIndex]: false,
83
85
  [EPathType.EPathTypeExtSubDomain]: false,
84
86
  [EPathType.EPathTypeCdcStream]: false,
87
+ [EPathType.EPathTypePersQueueGroup]: false,
85
88
  };
86
89
 
87
90
  export const isColumnEntityType = (type?: EPathType) =>