ydb-embedded-ui 1.13.1 → 1.14.0

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 (63) hide show
  1. package/CHANGELOG.md +42 -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/Acl/Acl.js +7 -1
  27. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -2
  28. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  29. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +8 -3
  30. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -1
  31. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +1 -1
  32. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +36 -10
  33. package/dist/containers/Tenant/Preview/Preview.js +15 -57
  34. package/dist/containers/Tenant/Preview/Preview.scss +4 -8
  35. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -41
  36. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +1 -5
  37. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.scss +1 -2
  38. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  39. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +9 -1
  40. package/dist/containers/Tenant/Tenant.scss +2 -50
  41. package/dist/containers/Tenant/Tenant.tsx +24 -22
  42. package/dist/containers/Tenant/utils/schema.ts +3 -0
  43. package/dist/containers/Tenant/utils/schemaActions.ts +1 -2
  44. package/dist/containers/Tenants/Tenants.js +12 -2
  45. package/dist/containers/UserSettings/UserSettings.tsx +26 -3
  46. package/dist/services/api.d.ts +19 -2
  47. package/dist/services/api.js +2 -2
  48. package/dist/setupTests.js +4 -0
  49. package/dist/store/reducers/executeQuery.js +4 -9
  50. package/dist/store/reducers/{preview.js → preview.ts} +22 -18
  51. package/dist/store/reducers/settings.js +3 -1
  52. package/dist/store/utils.ts +88 -0
  53. package/dist/types/api/query.ts +147 -0
  54. package/dist/types/api/schema.ts +235 -2
  55. package/dist/types/index.ts +33 -0
  56. package/dist/types/store/query.ts +9 -0
  57. package/dist/utils/{constants.js → constants.ts} +11 -6
  58. package/dist/utils/index.js +0 -24
  59. package/dist/utils/query.test.ts +189 -0
  60. package/dist/utils/query.ts +156 -0
  61. package/dist/utils/tests/providers.tsx +29 -0
  62. package/package.json +2 -2
  63. package/dist/store/utils.js +0 -51
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.14.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.13.2...v1.14.0) (2022-09-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Preview:** use modern query schema ([60bed3f](https://github.com/ydb-platform/ydb-embedded-ui/commit/60bed3fcb0fd76b869883742a2f2911201c0c226))
9
+ * **QueryEditor:** use modern query schema ([ecf38aa](https://github.com/ydb-platform/ydb-embedded-ui/commit/ecf38aa6b164ef7705e004aa77c8dab0e3164b51))
10
+ * **QueryResultTable:** component for displaying query result ([1b8be10](https://github.com/ydb-platform/ydb-embedded-ui/commit/1b8be10546ad9ae13b1043b2871b2aa110a5b6d4))
11
+ * **Storage:** experimental settings for disk colors ([b4291f4](https://github.com/ydb-platform/ydb-embedded-ui/commit/b4291f4ca19c588bc17eca50da51e898e6ccf581))
12
+ * **Tenant:** cdc streams info ([4cc773f](https://github.com/ydb-platform/ydb-embedded-ui/commit/4cc773f0351e3f1f6e279d1bebbb78329695e9ae))
13
+ * **Tenant:** cdc streams overview ([d1aed44](https://github.com/ydb-platform/ydb-embedded-ui/commit/d1aed4467135adaf01a06f8c4c4a4b3eb0b53106))
14
+ * **Tenant:** pq groups info & overview ([e1878a6](https://github.com/ydb-platform/ydb-embedded-ui/commit/e1878a6353933f74e62b204bf210f56a18a16c49))
15
+ * **Tenants:** display tenant nodes count ([72aef25](https://github.com/ydb-platform/ydb-embedded-ui/commit/72aef250006aae53d7704ff539b9eb537e6bfbd4))
16
+ * use schema param in sendQuery api ([01f9c71](https://github.com/ydb-platform/ydb-embedded-ui/commit/01f9c71190622279f03cd1c01d6b6e8e6739362a))
17
+
18
+
19
+ ### UI Updates
20
+
21
+ * **Storage:** new disks design ([26033d2](https://github.com/ydb-platform/ydb-embedded-ui/commit/26033d21e994c6ece7b3b8999d0fabbf82b43021))
22
+ * **Tenant:** consistent paddings for query results ([7f0a7c2](https://github.com/ydb-platform/ydb-embedded-ui/commit/7f0a7c28d18e48013223239b5780dbaca18f68a8))
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * always parse query error to string ([0fcabf7](https://github.com/ydb-platform/ydb-embedded-ui/commit/0fcabf7042adfc728f1ec651ebae50e8c40e9199))
28
+ * correct types & parsing for query api response ([d6a177c](https://github.com/ydb-platform/ydb-embedded-ui/commit/d6a177cd0e726f1d19e27c642e0a9c1d2832bbe0))
29
+ * **Preview:** display "table is empty" only for tables ([21a93c1](https://github.com/ydb-platform/ydb-embedded-ui/commit/21a93c1a070dbd04f7338537200cd2cb9849ff88))
30
+ * **Preview:** fix action type id ([7793fad](https://github.com/ydb-platform/ydb-embedded-ui/commit/7793fad6b618bfc4c35b85481b2a0b794698eaa1))
31
+ * **QueryResultTable:** don't display absent result as empty ([e2e5bfa](https://github.com/ydb-platform/ydb-embedded-ui/commit/e2e5bfaf0dbb89ec64766bf4ed5a4fab10ae8844))
32
+ * **QueryResultTable:** don't require theme prop ([c9686d4](https://github.com/ydb-platform/ydb-embedded-ui/commit/c9686d46eb2efdeb4bc093ecd44619e6c1a9c2fd))
33
+ * **Tenant:** input working query for 'select query' action in schema ([de152bd](https://github.com/ydb-platform/ydb-embedded-ui/commit/de152bdcc38fd6f4b1e5a5e6102c621f0155be36))
34
+ * **Tenant:** rename tab overview -> info ([2d13ffe](https://github.com/ydb-platform/ydb-embedded-ui/commit/2d13ffeb149765680c2887ea7ffb86d68ac92d5c))
35
+
36
+ ## [1.13.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.13.1...v1.13.2) (2022-09-05)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * **Tenant:** fix acl scroll ([161bc8d](https://github.com/ydb-platform/ydb-embedded-ui/commit/161bc8d507de126c1383a10713e2ffaaaf13301d))
42
+ * **Tenant:** fix layout after moving tabs ([6abfded](https://github.com/ydb-platform/ydb-embedded-ui/commit/6abfdedb97345b555be306d49ea2454f35de9bb4))
43
+ * **Tenant:** load root if cahced path is not in tree ([2d86044](https://github.com/ydb-platform/ydb-embedded-ui/commit/2d8604464711a638dbd20cf8a14142b0de3e3a95))
44
+
3
45
  ## [1.13.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.13.0...v1.13.1) (2022-09-02)
4
46
 
5
47
 
@@ -0,0 +1,3 @@
1
+ <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
2
+ <path d="M437.2 403.5L320 215V64h8c13.3 0 24-10.7 24-24V24c0-13.3-10.7-24-24-24H120c-13.3 0-24 10.7-24 24v16c0 13.3 10.7 24 24 24h8v151L10.8 403.5C-18.5 450.6 15.3 512 70.9 512h306.2c55.7 0 89.4-61.5 60.1-108.5zM137.9 320l48.2-77.6c3.7-5.2 5.8-11.6 5.8-18.4V64h64v160c0 6.9 2.2 13.2 5.8 18.4l48.2 77.6h-172z"/>
3
+ </svg>
@@ -0,0 +1,15 @@
1
+ import type {TDirEntry} from '../../../types/api/schema';
2
+ import {formatDateTime} from '../../../utils';
3
+
4
+ import {createInfoFormatter} from '../utils';
5
+
6
+ export const formatCommonItem = createInfoFormatter<TDirEntry>({
7
+ values: {
8
+ PathType: (value) => value?.substring('EPathType'.length),
9
+ CreateStep: formatDateTime,
10
+ },
11
+ labels: {
12
+ PathType: 'Type',
13
+ CreateStep: 'Created',
14
+ },
15
+ });
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './schema';
@@ -0,0 +1,43 @@
1
+ import type {
2
+ TCdcStreamDescription,
3
+ TIndexDescription,
4
+ TPersQueueGroupDescription,
5
+ } from '../../../types/api/schema';
6
+ import {formatNumber} from '../../../utils';
7
+ import {HOUR_IN_SECONDS} from '../../../utils/constants';
8
+
9
+ import {createInfoFormatter} from '../utils';
10
+
11
+ export const formatTableIndexItem = createInfoFormatter<TIndexDescription>({
12
+ values: {
13
+ Type: (value) => value?.substring(10), // trims EIndexType prefix
14
+ State: (value) => value?.substring(11), // trims EIndexState prefix
15
+ KeyColumnNames: (value) => value?.join(', '),
16
+ DataColumnNames: (value) => value?.join(', '),
17
+ },
18
+ labels: {
19
+ KeyColumnNames: 'Columns',
20
+ DataColumnNames: 'Includes',
21
+ },
22
+ });
23
+
24
+ export const formatCdcStreamItem = createInfoFormatter<TCdcStreamDescription>({
25
+ values: {
26
+ Mode: (value) => value?.substring('ECdcStreamMode'.length),
27
+ Format: (value) => value?.substring('ECdcStreamFormat'.length),
28
+ },
29
+ });
30
+
31
+ export const formatPQGroupItem = createInfoFormatter<TPersQueueGroupDescription>({
32
+ values: {
33
+ Partitions: (value) => formatNumber(value?.length || 0),
34
+ PQTabletConfig: (value) => {
35
+ const hours = Math.round(value.PartitionConfig.LifetimeSeconds / HOUR_IN_SECONDS * 100) / 100;
36
+ return `${formatNumber(hours)} hours`;
37
+ },
38
+ },
39
+ labels: {
40
+ Partitions: 'Partitions count',
41
+ PQTabletConfig: 'Retention',
42
+ },
43
+ });
@@ -0,0 +1,44 @@
1
+ import type {TEvDescribeSchemeResult, TCdcStreamDescription} from '../../../types/api/schema';
2
+
3
+ import {formatCdcStreamItem, formatCommonItem} from '../formatters';
4
+ import {InfoViewer, InfoViewerItem} from '..';
5
+
6
+ const DISPLAYED_FIELDS: Set<keyof TCdcStreamDescription> = new Set([
7
+ 'Mode',
8
+ 'Format',
9
+ ]);
10
+
11
+ interface CDCStreamInfoProps {
12
+ data?: TEvDescribeSchemeResult;
13
+ }
14
+
15
+ export const CDCStreamInfo = ({data}: CDCStreamInfoProps) => {
16
+ if (!data) {
17
+ return (
18
+ <div className="error">No CDC Stream data</div>
19
+ );
20
+ }
21
+
22
+ const TableIndex = data.PathDescription?.CdcStreamDescription;
23
+ const info: Array<InfoViewerItem> = [];
24
+
25
+ info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType));
26
+ info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep));
27
+
28
+ let key: keyof TCdcStreamDescription;
29
+ for (key in TableIndex) {
30
+ if (DISPLAYED_FIELDS.has(key)) {
31
+ info.push(formatCdcStreamItem(key, TableIndex?.[key]));
32
+ }
33
+ }
34
+
35
+ return (
36
+ <>
37
+ {info.length ? (
38
+ <InfoViewer info={info}></InfoViewer>
39
+ ) : (
40
+ <>Empty</>
41
+ )}
42
+ </>
43
+ );
44
+ };
@@ -0,0 +1,34 @@
1
+ import type {TEvDescribeSchemeResult} from '../../../types/api/schema';
2
+
3
+ import {formatCommonItem, formatPQGroupItem} from '../formatters';
4
+ import {InfoViewer, InfoViewerItem} from '..';
5
+
6
+ interface PersQueueGrouopInfoProps {
7
+ data?: TEvDescribeSchemeResult;
8
+ }
9
+
10
+ export const PersQueueGroupInfo = ({data}: PersQueueGrouopInfoProps) => {
11
+ if (!data) {
12
+ return (
13
+ <div className="error">No PersQueueGroup data</div>
14
+ );
15
+ }
16
+
17
+ const pqGroup = data.PathDescription?.PersQueueGroup;
18
+ const info: Array<InfoViewerItem> = [];
19
+
20
+ info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType));
21
+
22
+ info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || []));
23
+ info.push(formatPQGroupItem('PQTabletConfig', pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}}));
24
+
25
+ return (
26
+ <>
27
+ {info.length ? (
28
+ <InfoViewer info={info}></InfoViewer>
29
+ ) : (
30
+ <>Empty</>
31
+ )}
32
+ </>
33
+ );
34
+ };
@@ -1,5 +1,7 @@
1
- import type {TEvDescribeSchemeResult, TIndexDescription} from '../../types/api/schema';
2
- import {InfoViewer, createInfoFormatter, InfoViewerItem} from '../InfoViewer';
1
+ import type {TEvDescribeSchemeResult, TIndexDescription} from '../../../types/api/schema';
2
+
3
+ import {formatTableIndexItem} from '../formatters';
4
+ import {InfoViewer, InfoViewerItem} from '..';
3
5
 
4
6
  const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
5
7
  'Type',
@@ -9,24 +11,11 @@ const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
9
11
  'DataColumnNames',
10
12
  ]);
11
13
 
12
- const formatItem = createInfoFormatter<TIndexDescription>({
13
- values: {
14
- Type: (value) => value?.substring(10), // trims EIndexType prefix
15
- State: (value) => value?.substring(11), // trims EIndexState prefix
16
- KeyColumnNames: (value) => value?.join(', '),
17
- DataColumnNames: (value) => value?.join(', '),
18
- },
19
- labels: {
20
- KeyColumnNames: 'Columns',
21
- DataColumnNames: 'Includes',
22
- },
23
- });
24
-
25
- interface IndexInfoViewerProps {
14
+ interface TableIndexInfoProps {
26
15
  data?: TEvDescribeSchemeResult;
27
16
  }
28
17
 
29
- export const IndexInfoViewer = ({data}: IndexInfoViewerProps) => {
18
+ export const TableIndexInfo = ({data}: TableIndexInfoProps) => {
30
19
  if (!data) {
31
20
  return (
32
21
  <div className="error">no index data</div>
@@ -39,7 +28,7 @@ export const IndexInfoViewer = ({data}: IndexInfoViewerProps) => {
39
28
  let key: keyof TIndexDescription;
40
29
  for (key in TableIndex) {
41
30
  if (DISPLAYED_FIELDS.has(key)) {
42
- info.push(formatItem(key, TableIndex?.[key]));
31
+ info.push(formatTableIndexItem(key, TableIndex?.[key]));
43
32
  }
44
33
  }
45
34
 
@@ -0,0 +1,3 @@
1
+ export * from './CDCStreamInfo';
2
+ export * from './TableIndexInfo';
3
+ export * from './PersQueueGroupInfo';
@@ -0,0 +1,44 @@
1
+ import type {TEvDescribeSchemeResult, TCdcStreamDescription} from '../../../types/api/schema';
2
+
3
+ import {InfoViewer, InfoViewerItem} from '..';
4
+ import {formatCdcStreamItem, formatCommonItem} from '../formatters';
5
+
6
+ const DISPLAYED_FIELDS: Set<keyof TCdcStreamDescription> = new Set([
7
+ 'Mode',
8
+ 'Format',
9
+ ]);
10
+
11
+ interface CDCStreamOverviewProps {
12
+ data?: TEvDescribeSchemeResult;
13
+ }
14
+
15
+ export const CDCStreamOverview = ({data}: CDCStreamOverviewProps) => {
16
+ if (!data) {
17
+ return (
18
+ <div className="error">No CDC Stream data</div>
19
+ );
20
+ }
21
+
22
+ const TableIndex = data.PathDescription?.CdcStreamDescription;
23
+ const info: Array<InfoViewerItem> = [];
24
+
25
+ info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType));
26
+ info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep));
27
+
28
+ let key: keyof TCdcStreamDescription;
29
+ for (key in TableIndex) {
30
+ if (DISPLAYED_FIELDS.has(key)) {
31
+ info.push(formatCdcStreamItem(key, TableIndex?.[key]));
32
+ }
33
+ }
34
+
35
+ return (
36
+ <>
37
+ {info.length ? (
38
+ <InfoViewer info={info}></InfoViewer>
39
+ ) : (
40
+ <>Empty</>
41
+ )}
42
+ </>
43
+ );
44
+ };
@@ -0,0 +1,35 @@
1
+ import type {TEvDescribeSchemeResult} from '../../../types/api/schema';
2
+
3
+ import {formatCommonItem, formatPQGroupItem} from '../formatters';
4
+ import {InfoViewer, InfoViewerItem} from '..';
5
+
6
+ interface PersQueueGroupOverviewProps {
7
+ data?: TEvDescribeSchemeResult;
8
+ }
9
+
10
+ export const PersQueueGroupOverview = ({data}: PersQueueGroupOverviewProps) => {
11
+ if (!data) {
12
+ return (
13
+ <div className="error">No PersQueueGroup data</div>
14
+ );
15
+ }
16
+
17
+ const pqGroup = data.PathDescription?.PersQueueGroup;
18
+ const info: Array<InfoViewerItem> = [];
19
+
20
+ info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType));
21
+ info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep));
22
+
23
+ info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || []));
24
+ info.push(formatPQGroupItem('PQTabletConfig', pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}}));
25
+
26
+ return (
27
+ <>
28
+ {info.length ? (
29
+ <InfoViewer info={info}></InfoViewer>
30
+ ) : (
31
+ <>Empty</>
32
+ )}
33
+ </>
34
+ );
35
+ };
@@ -0,0 +1,2 @@
1
+ export * from './CDCStreamOverview';
2
+ export * from './PersQueueGroupOverview';
@@ -0,0 +1,33 @@
1
+ import React, {useEffect} from 'react';
2
+ import {useDispatch} from 'react-redux';
3
+
4
+ import {showTooltip, hideTooltip} from '../../../store/reducers/tooltip';
5
+
6
+ import {b} from '../QueryResultTable';
7
+
8
+ interface CellProps {
9
+ className?: string;
10
+ value: string;
11
+ }
12
+
13
+ export const Cell = React.memo(function Cell(props: CellProps) {
14
+ const {
15
+ className,
16
+ value,
17
+ } = props;
18
+
19
+ const dispatch = useDispatch();
20
+
21
+ useEffect(() => () => {
22
+ dispatch(hideTooltip());
23
+ }, [dispatch]);
24
+
25
+ return (
26
+ <span
27
+ className={b('cell', className)}
28
+ onClick={(e) => dispatch(showTooltip(e.target, value, 'cell'))}
29
+ >
30
+ {value}
31
+ </span>
32
+ )
33
+ });
@@ -0,0 +1 @@
1
+ export * from './Cell';
@@ -0,0 +1,11 @@
1
+ @import '../../styles/mixins.scss';
2
+
3
+ .ydb-query-result-table {
4
+ &__cell {
5
+ @include cell-container;
6
+ }
7
+
8
+ &__message {
9
+ padding: 15px 10px;
10
+ }
11
+ }
@@ -0,0 +1,115 @@
1
+ import {useMemo} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import DataTable from '@yandex-cloud/react-data-table';
5
+ import type {Column, DataTableProps, Settings} from '@yandex-cloud/react-data-table';
6
+
7
+ import type {ColumnType, KeyValueRow} from '../../types/api/query';
8
+ import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
9
+ import {getColumnType, prepareQueryResponse} from '../../utils/query';
10
+ import {isNumeric} from '../../utils/utils';
11
+
12
+ import {Cell} from './Cell';
13
+
14
+ import i18n from './i18n';
15
+ import './QueryResultTable.scss';
16
+
17
+ const TABLE_SETTINGS: Settings = {
18
+ ...DEFAULT_TABLE_SETTINGS,
19
+ stripedRows: true,
20
+ };
21
+
22
+ export const b = cn('ydb-query-result-table');
23
+
24
+ const prepareTypedColumns = (columns: ColumnType[]) => {
25
+ if (!columns.length) {
26
+ return [];
27
+ }
28
+
29
+ return columns.map(({name, type}) => {
30
+ const columnType = getColumnType(type);
31
+
32
+ const column: Column<KeyValueRow> = {
33
+ name,
34
+ align: columnType === 'number' ? DataTable.RIGHT : DataTable.LEFT,
35
+ sortAccessor: (row) => {
36
+ const value = row[name];
37
+ if (value === undefined || value === null) return null;
38
+ return columnType === 'number' ? BigInt(value) : value;
39
+ },
40
+ render: ({value}) => <Cell value={value as string} />,
41
+ };
42
+
43
+ return column;
44
+ });
45
+ };
46
+
47
+ const prepareGenericColumns = (data: KeyValueRow[]) => {
48
+ if (!data.length) {
49
+ return [];
50
+ }
51
+
52
+ return Object.keys(data[0]).map((name) => {
53
+ const column: Column<KeyValueRow> = {
54
+ name,
55
+ align: isNumeric(data[0][name]) ? DataTable.RIGHT : DataTable.LEFT,
56
+ sortAccessor: (row) => isNumeric(row[name]) ? Number(row[name]) : row[name],
57
+ render: ({value}) => <Cell value={value as string} />,
58
+ };
59
+
60
+ return column;
61
+ });
62
+ };
63
+
64
+ const getRowIndex = (_: unknown, index: number) => index
65
+
66
+ interface QueryResultTableProps extends Omit<DataTableProps<KeyValueRow>, 'data' | 'columns' | 'theme'> {
67
+ data?: KeyValueRow[];
68
+ columns?: ColumnType[];
69
+ }
70
+
71
+ export const QueryResultTable = (props: QueryResultTableProps) => {
72
+ const {
73
+ columns: rawColumns,
74
+ data: rawData,
75
+ settings: settingsMix,
76
+ ...restProps
77
+ } = props;
78
+
79
+ const data = useMemo(() => prepareQueryResponse(rawData), [rawData]);
80
+ const columns = useMemo(() => {
81
+ return rawColumns ?
82
+ prepareTypedColumns(rawColumns) :
83
+ prepareGenericColumns(data);
84
+ }, [data, rawColumns]);
85
+ const settings = useMemo(() => ({
86
+ ...TABLE_SETTINGS,
87
+ ...settingsMix,
88
+ }), [settingsMix]);
89
+
90
+ // empty data is expected to be be an empty array
91
+ // undefined data is not rendered at all
92
+ if (!Array.isArray(rawData)) {
93
+ return null;
94
+ }
95
+
96
+ if (!columns.length) {
97
+ return (
98
+ <div className={b('message')}>
99
+ {i18n('empty')}
100
+ </div>
101
+ );
102
+ }
103
+
104
+ return (
105
+ <DataTable
106
+ theme="yandex-cloud"
107
+ data={data}
108
+ columns={columns}
109
+ settings={settings}
110
+ // prevent accessing row.id in case it is present but is not the PK (i.e. may repeat)
111
+ rowKey={getRowIndex}
112
+ {...restProps}
113
+ />
114
+ );
115
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "empty": "Table is empty"
3
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-query-result-table';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,3 @@
1
+ {
2
+ "empty": "Таблица пустая"
3
+ }
@@ -0,0 +1 @@
1
+ export * from './QueryResultTable';
@@ -25,6 +25,7 @@ body,
25
25
 
26
26
  :root {
27
27
  --yc-color-infographics-yellow-light: rgba(255, 199, 0, 0.15);
28
+ --yc-color-infographics-yellow-medium: rgba(255, 219, 77, 0.4);
28
29
  --yc-color-base-warning-orange: #ff922e;
29
30
 
30
31
  --data-table-row-height: 40px;
@@ -1,4 +1,8 @@
1
1
  .storage-disk-progress-bar {
2
+ $border-width: 2px;
3
+ $outer-border-radius: 4px;
4
+ $inner-border-radius: $outer-border-radius - $border-width;
5
+
2
6
  $block: &;
3
7
  position: relative;
4
8
 
@@ -9,8 +13,8 @@
9
13
 
10
14
  vertical-align: top;
11
15
 
12
- border: 1px solid var(--yc-color-infographics-misc-medium);
13
- border-radius: 2px;
16
+ border: $border-width solid var(--yc-color-infographics-misc-heavy);
17
+ border-radius: $outer-border-radius;
14
18
  background-color: var(--yc-color-infographics-misc-light);
15
19
 
16
20
  #{$block}__filled {
@@ -18,15 +22,20 @@
18
22
  }
19
23
 
20
24
  &_green {
21
- border: 1px solid var(--yc-color-infographics-positive-heavy);
25
+ border-color: var(--yc-color-infographics-positive-heavy);
22
26
  background-color: var(--yc-color-infographics-positive-light);
23
27
  #{$block}__filled {
24
- background-color: var(--yc-color-infographics-positive-heavy);
28
+ background-color: var(--yc-color-infographics-positive-medium);
29
+
30
+ .yc-root_theme_dark & {
31
+ // the common medium green is too bright for this case
32
+ background-color: rgb(124, 227, 121, 0.4);
33
+ }
25
34
  }
26
35
  }
27
36
 
28
37
  &_blue {
29
- border: 1px solid var(--yc-color-infographics-info-medium);
38
+ border-color: var(--yc-color-infographics-info-heavy);
30
39
  background-color: var(--yc-color-infographics-info-light);
31
40
  #{$block}__filled {
32
41
  background-color: var(--yc-color-infographics-info-medium);
@@ -34,25 +43,25 @@
34
43
  }
35
44
 
36
45
  &_yellow {
37
- border: 1px solid var(--yc-color-infographics-warning-heavy);
46
+ border-color: var(--yc-color-infographics-warning-heavy);
38
47
  background-color: var(--yc-color-infographics-yellow-light);
39
48
  #{$block}__filled {
40
- background-color: var(--yc-color-infographics-warning-heavy);
49
+ background-color: var(--yc-color-infographics-yellow-medium);
41
50
  }
42
51
  }
43
52
 
44
53
  &_orange {
45
- border: 1px solid var(--yc-color-base-warning-orange);
54
+ border-color: var(--yc-color-base-warning-orange);
46
55
  background-color: var(--yc-color-infographics-warning-light);
47
56
  #{$block}__filled {
48
- background-color: var(--yc-color-base-warning-orange);
57
+ background-color: var(--yc-color-infographics-warning-medium);
49
58
  }
50
59
  }
51
60
  &_red {
52
- border: 1px solid var(--yc-color-infographics-danger-heavy);
61
+ border-color: var(--yc-color-infographics-danger-heavy);
53
62
  background-color: var(--yc-color-infographics-danger-light);
54
63
  #{$block}__filled {
55
- background-color: var(--yc-color-infographics-danger-heavy);
64
+ background-color: var(--yc-color-infographics-danger-medium);
56
65
  }
57
66
  }
58
67
 
@@ -63,14 +72,22 @@
63
72
 
64
73
  height: 100%;
65
74
 
66
- border-radius: 2px 0 0 2px;
75
+ border-radius: $inner-border-radius 0 0 $inner-border-radius;
76
+
77
+ #{$block}_inverted & {
78
+ right: 0;
79
+ left: auto;
80
+
81
+ border-radius: 0 $inner-border-radius $inner-border-radius 0;
82
+ }
67
83
  }
68
84
  &__filled-title {
69
85
  position: absolute;
70
86
  z-index: 2;
71
87
 
72
88
  font-size: var(--yc-text-body-1-font-size);
73
- line-height: inherit;
89
+ // bar height minus borders
90
+ line-height: calc(var(--yc-text-body-2-line-height) - #{$border-width * 2});
74
91
 
75
92
  color: inherit;
76
93
  }
@@ -79,8 +96,16 @@
79
96
  display: flex;
80
97
  justify-content: center;
81
98
 
82
- height: 100%;
99
+ // extend active area to include borders
100
+ height: var(--yc-text-body-2-line-height);
101
+ margin: -$border-width;
102
+ padding: $border-width;
83
103
 
84
104
  color: inherit;
105
+ border-radius: $outer-border-radius;
106
+
107
+ &:hover {
108
+ color: inherit;
109
+ }
85
110
  }
86
111
  }