ydb-embedded-ui 2.2.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/assets/icons/shield.svg +3 -0
  3. package/dist/components/Errors/403/AccessDenied.tsx +19 -0
  4. package/dist/components/Errors/403/index.ts +1 -0
  5. package/dist/components/Errors/i18n/en.json +4 -0
  6. package/dist/components/Errors/i18n/index.ts +11 -0
  7. package/dist/components/Errors/i18n/ru.json +4 -0
  8. package/dist/components/NodesViewer/NodesViewer.js +1 -1
  9. package/dist/components/QueryResultTable/QueryResultTable.tsx +16 -21
  10. package/dist/{containers/Storage/StorageFilter/StorageFilter.tsx → components/Search/Search.tsx} +22 -22
  11. package/dist/components/Search/index.ts +1 -0
  12. package/dist/containers/App/App.scss +5 -1
  13. package/dist/containers/Nodes/Nodes.js +6 -1
  14. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +7 -5
  15. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +1 -11
  16. package/dist/containers/Storage/Pdisk/Pdisk.scss +15 -8
  17. package/dist/containers/Storage/Pdisk/Pdisk.tsx +22 -14
  18. package/dist/containers/Storage/Storage.js +39 -50
  19. package/dist/containers/Storage/StorageGroups/StorageGroups.scss +1 -4
  20. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +27 -2
  21. package/dist/containers/Storage/StorageGroups/i18n/en.json +2 -1
  22. package/dist/containers/Storage/StorageGroups/i18n/ru.json +2 -1
  23. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +14 -12
  24. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -5
  25. package/dist/containers/Storage/Vdisk/Vdisk.js +36 -23
  26. package/dist/containers/Storage/Vdisk/Vdisk.scss +6 -0
  27. package/dist/containers/TabletsFilters/TabletsFilters.js +5 -0
  28. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +6 -0
  29. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +82 -0
  30. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +6 -0
  31. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/index.ts +11 -0
  32. package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +6 -0
  33. package/dist/containers/Tenant/Diagnostics/Consumers/index.ts +1 -0
  34. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +7 -0
  35. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +29 -11
  36. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -8
  37. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/Details.tsx +55 -0
  38. package/dist/containers/Tenant/Diagnostics/Healthcheck/Details/index.ts +1 -0
  39. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.scss +5 -5
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +16 -6
  41. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/{IssueViewer.scss → IssueTree.scss} +3 -54
  42. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTree.tsx +87 -0
  43. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.scss +50 -0
  44. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/IssueTreeItem.tsx +25 -0
  45. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueTreeItem/index.ts +1 -0
  46. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/Preview.tsx +13 -16
  47. package/dist/containers/Tenant/Diagnostics/Healthcheck/{IssuePreview/IssuePreview.tsx → Preview/PreviewItem/PreviewItem.tsx} +6 -8
  48. package/dist/containers/Tenant/Diagnostics/Healthcheck/Preview/PreviewItem/index.ts +1 -0
  49. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +34 -19
  50. package/dist/containers/Tenant/Preview/Preview.scss +6 -0
  51. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -9
  52. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +2 -2
  53. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -1
  54. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +27 -20
  55. package/dist/containers/Tenant/Tenant.tsx +14 -16
  56. package/dist/containers/Tenants/Tenants.js +1 -1
  57. package/dist/store/reducers/describe.ts +71 -0
  58. package/dist/store/reducers/healthcheckInfo.ts +123 -0
  59. package/dist/store/reducers/olapStats.js +13 -0
  60. package/dist/store/reducers/schema.js +43 -1
  61. package/dist/store/reducers/storage.js +27 -17
  62. package/dist/store/reducers/tenant.js +3 -1
  63. package/dist/store/utils.ts +21 -13
  64. package/dist/styles/mixins.scss +1 -1
  65. package/dist/types/api/consumers.ts +3 -0
  66. package/dist/types/api/healthcheck.ts +1 -1
  67. package/dist/types/api/storage.ts +35 -10
  68. package/dist/types/store/healthcheck.ts +5 -1
  69. package/dist/types/store/storage.ts +1 -0
  70. package/dist/utils/hooks/useAutofetcher.ts +9 -3
  71. package/package.json +1 -1
  72. package/dist/containers/Storage/StorageFilter/index.ts +0 -1
  73. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuePreview/index.ts +0 -1
  74. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/IssuesList.tsx +0 -62
  75. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesList/index.ts +0 -1
  76. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +0 -151
  77. package/dist/store/reducers/describe.js +0 -45
  78. package/dist/store/reducers/healthcheckInfo.js +0 -45
@@ -4,10 +4,15 @@ import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Loader} from '@gravity-ui/uikit';
6
6
 
7
- import {getHealthcheckInfo} from '../../../../store/reducers/healthcheckInfo';
7
+ import {SelfCheckResult} from '../../../../types/api/healthcheck';
8
+ import {
9
+ getHealthcheckInfo,
10
+ selectIssuesTreeById,
11
+ selectIssuesTreesRoots,
12
+ } from '../../../../store/reducers/healthcheckInfo';
8
13
  import {useAutofetcher} from '../../../../utils/hooks';
9
14
 
10
- import {IssuesList} from './IssuesList';
15
+ import {Details} from './Details';
11
16
  import {Preview} from './Preview';
12
17
 
13
18
  import i18n from './i18n';
@@ -29,6 +34,11 @@ export const Healthcheck = (props: HealthcheckProps) => {
29
34
  const dispatch = useDispatch();
30
35
 
31
36
  const {data, loading, wasLoaded, error} = useSelector((state: any) => state.healthcheckInfo);
37
+ const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
38
+
39
+ const issuesTreesRoots = useSelector(selectIssuesTreesRoots);
40
+ const expandedIssueTree = useSelector((state) => selectIssuesTreeById(state, expandedIssueId));
41
+
32
42
  const {autorefresh} = useSelector((state: any) => state.schema);
33
43
 
34
44
  const fetchHealthcheck = useCallback(() => {
@@ -61,15 +71,15 @@ export const Healthcheck = (props: HealthcheckProps) => {
61
71
  if (data && data['self_check_result']) {
62
72
  return preview ? (
63
73
  <Preview
64
- data={data}
74
+ issuesTrees={issuesTreesRoots}
75
+ selfCheckResult={selfCheckResult}
65
76
  loading={loading}
66
77
  onShowMore={showMoreHandler}
67
78
  onUpdate={fetchHealthcheck}
68
79
  />
69
80
  ) : (
70
- <IssuesList
71
- data={data}
72
- expandedIssueId={expandedIssueId}
81
+ <Details
82
+ issueTree={expandedIssueTree}
73
83
  loading={loading}
74
84
  onUpdate={fetchHealthcheck}
75
85
  />
@@ -1,56 +1,5 @@
1
1
  @import '../../../../../styles/mixins.scss';
2
2
 
3
- .issue {
4
- display: flex;
5
- justify-content: space-between;
6
- align-items: center;
7
-
8
- height: 40px;
9
-
10
- cursor: pointer;
11
-
12
- &__field {
13
- display: flex;
14
- overflow: hidden;
15
-
16
- &_status {
17
- display: flex;
18
-
19
- white-space: nowrap;
20
- }
21
- &_additional {
22
- width: max-content;
23
-
24
- cursor: pointer;
25
-
26
- color: var(--yc-color-text-link);
27
-
28
- &:hover {
29
- color: var(--yc-color-text-link-hover);
30
- }
31
- }
32
- &_message {
33
- overflow: hidden;
34
- flex-shrink: 0;
35
-
36
- width: 300px;
37
-
38
- white-space: normal;
39
- }
40
- }
41
-
42
- &__field-tooltip {
43
- &#{&} {
44
- min-width: 500px;
45
- max-width: 500px;
46
- }
47
- }
48
-
49
- &__field-label {
50
- color: var(--yc-color-text-secondary);
51
- }
52
- }
53
-
54
3
  .indicator {
55
4
  width: 12px;
56
5
  height: 12px;
@@ -86,12 +35,12 @@
86
35
  }
87
36
  }
88
37
 
89
- .issue-viewer {
38
+ .issue-tree {
90
39
  display: flex;
91
40
 
92
41
  width: 820px;
93
42
 
94
- &__tree {
43
+ &__block {
95
44
  width: 100%;
96
45
  }
97
46
 
@@ -163,7 +112,7 @@
163
112
  padding-left: 0 !important;
164
113
  }
165
114
 
166
- .issue-viewer__info-panel {
115
+ .issue-tree__info-panel {
167
116
  margin-left: $calculated-margin;
168
117
  }
169
118
  }
@@ -0,0 +1,87 @@
1
+ import {useCallback, useState} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+ import _omit from 'lodash/omit';
4
+
5
+ // @ts-ignore
6
+ import JSONTree from 'react-json-inspector';
7
+
8
+ import {TreeView} from 'ydb-ui-components';
9
+
10
+ import {IIssuesTree} from '../../../../../types/store/healthcheck';
11
+
12
+ import {IssueTreeItem} from './IssueTreeItem';
13
+
14
+ import './IssueTree.scss';
15
+
16
+ const b = cn('issue-tree');
17
+
18
+ interface IssuesViewerProps {
19
+ issueTree: IIssuesTree;
20
+ }
21
+
22
+ const IssueTree = ({issueTree}: IssuesViewerProps) => {
23
+ const [collapsedIssues, setCollapsedIssues] = useState<Record<string, boolean>>({});
24
+
25
+ const renderTree = useCallback(
26
+ (data: IIssuesTree[]) => {
27
+ return data.map((item) => {
28
+ const {id} = item;
29
+ const {status, message, type, reasonsItems, level, ...rest} = item;
30
+
31
+ const isCollapsed =
32
+ typeof collapsedIssues[id] === 'undefined' || collapsedIssues[id];
33
+
34
+ const toggleCollapsed = () => {
35
+ setCollapsedIssues((collapsedIssues) => ({
36
+ ...collapsedIssues,
37
+ [id]: !isCollapsed,
38
+ }));
39
+ };
40
+
41
+ return (
42
+ <TreeView
43
+ key={id}
44
+ name={<IssueTreeItem status={status} message={message} type={type} />}
45
+ collapsed={isCollapsed}
46
+ hasArrow={true}
47
+ onClick={toggleCollapsed}
48
+ onArrowClick={toggleCollapsed}
49
+ level={level - 1}
50
+ >
51
+ {renderInfoPanel(_omit(rest, ['reason']))}
52
+ {renderTree(reasonsItems || [])}
53
+ </TreeView>
54
+ );
55
+ });
56
+ },
57
+ [issueTree, collapsedIssues],
58
+ );
59
+
60
+ const renderInfoPanel = useCallback(
61
+ (info) => {
62
+ if (!info) {
63
+ return null;
64
+ }
65
+
66
+ return (
67
+ <div className={b('info-panel')}>
68
+ <JSONTree
69
+ data={info}
70
+ search={false}
71
+ isExpanded={() => true}
72
+ className={b('inspector')}
73
+ />
74
+ </div>
75
+ );
76
+ },
77
+ [issueTree],
78
+ );
79
+
80
+ return (
81
+ <div className={b()}>
82
+ <div className={b('block')}>{renderTree([issueTree])}</div>
83
+ </div>
84
+ );
85
+ };
86
+
87
+ export default IssueTree;
@@ -0,0 +1,50 @@
1
+ .issue-tree-item {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+
6
+ height: 40px;
7
+
8
+ cursor: pointer;
9
+
10
+ &__field {
11
+ display: flex;
12
+ overflow: hidden;
13
+
14
+ &_status {
15
+ display: flex;
16
+
17
+ white-space: nowrap;
18
+ }
19
+ &_additional {
20
+ width: max-content;
21
+
22
+ cursor: pointer;
23
+
24
+ color: var(--yc-color-text-link);
25
+
26
+ &:hover {
27
+ color: var(--yc-color-text-link-hover);
28
+ }
29
+ }
30
+ &_message {
31
+ overflow: hidden;
32
+ flex-shrink: 0;
33
+
34
+ width: 300px;
35
+
36
+ white-space: normal;
37
+ }
38
+ }
39
+
40
+ &__field-tooltip {
41
+ &#{&} {
42
+ min-width: 500px;
43
+ max-width: 500px;
44
+ }
45
+ }
46
+
47
+ &__field-label {
48
+ color: var(--yc-color-text-secondary);
49
+ }
50
+ }
@@ -0,0 +1,25 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import EntityStatus from '../../../../../../components/EntityStatus/EntityStatus';
4
+
5
+ import './IssueTreeItem.scss';
6
+
7
+ const b = cn('issue-tree-item');
8
+
9
+ interface IssueRowProps {
10
+ status: string;
11
+ message: string;
12
+ type: string;
13
+ onClick?: VoidFunction;
14
+ }
15
+
16
+ export const IssueTreeItem = ({status, message, type, onClick}: IssueRowProps) => {
17
+ return (
18
+ <div className={b()} onClick={onClick}>
19
+ <div className={b('field', {status: true})}>
20
+ <EntityStatus mode="icons" status={status} name={type} />
21
+ </div>
22
+ <div className={b('field', {message: true})}>{message}</div>
23
+ </div>
24
+ );
25
+ };
@@ -0,0 +1 @@
1
+ export * from './IssueTreeItem';
@@ -1,4 +1,3 @@
1
- import {useMemo} from 'react';
2
1
  import cn from 'bem-cn-lite';
3
2
 
4
3
  import {Button, Icon} from '@gravity-ui/uikit';
@@ -6,34 +5,28 @@ import {Button, Icon} from '@gravity-ui/uikit';
6
5
  import updateArrow from '../../../../../assets/icons/update-arrow.svg';
7
6
 
8
7
  import {SelfCheckResult} from '../../../../../types/api/healthcheck';
9
- import type {IHealthCheck} from '../../../../../types/store/healthcheck';
8
+ import type {IIssuesTree} from '../../../../../types/store/healthcheck';
10
9
 
11
- import {IssuePreview} from '../IssuePreview';
10
+ import {PreviewItem} from './PreviewItem';
12
11
 
13
12
  import i18n from '../i18n';
14
13
 
15
14
  const b = cn('healthcheck');
16
15
 
17
16
  interface PreviewProps {
18
- data?: IHealthCheck;
17
+ selfCheckResult: SelfCheckResult;
18
+ issuesTrees?: IIssuesTree[];
19
19
  loading?: boolean;
20
- onUpdate: VoidFunction;
21
20
  onShowMore?: (id: string) => void;
21
+ onUpdate: VoidFunction;
22
22
  }
23
23
 
24
24
  export const Preview = (props: PreviewProps) => {
25
- const {data, loading, onShowMore, onUpdate} = props;
25
+ const {selfCheckResult, issuesTrees, loading, onShowMore, onUpdate} = props;
26
26
 
27
- const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
28
27
  const isStatusOK = selfCheckResult === SelfCheckResult.GOOD;
29
28
 
30
- const issuesLog = data?.issue_log;
31
- const firstLevelIssues = useMemo(
32
- () => issuesLog?.filter(({level}) => level === 1),
33
- [issuesLog],
34
- );
35
-
36
- if (!data) {
29
+ if (!issuesTrees) {
37
30
  return null;
38
31
  }
39
32
 
@@ -58,8 +51,12 @@ export const Preview = (props: PreviewProps) => {
58
51
  <div className={b('preview-content')}>
59
52
  {isStatusOK
60
53
  ? i18n('status_message.ok')
61
- : firstLevelIssues?.map((issue) => (
62
- <IssuePreview key={issue.id} data={issue} onShowMore={onShowMore} />
54
+ : issuesTrees?.map((issueTree) => (
55
+ <PreviewItem
56
+ key={issueTree.id}
57
+ data={issueTree}
58
+ onShowMore={onShowMore}
59
+ />
63
60
  ))}
64
61
  </div>
65
62
  );
@@ -2,19 +2,19 @@ import cn from 'bem-cn-lite';
2
2
 
3
3
  import {Link, Text} from '@gravity-ui/uikit';
4
4
 
5
- import EntityStatus from '../../../../../components/EntityStatus/EntityStatus';
6
- import {IssueLog} from '../../../../../types/api/healthcheck';
5
+ import EntityStatus from '../../../../../../components/EntityStatus/EntityStatus';
6
+ import {IssueLog} from '../../../../../../types/api/healthcheck';
7
7
 
8
- import i18n from '../i18n';
8
+ import i18n from '../../i18n';
9
9
 
10
10
  const b = cn('healthcheck');
11
11
 
12
- interface IssuePreviewProps {
12
+ interface PreviewItemProps {
13
13
  data?: IssueLog;
14
14
  onShowMore?: (id: string) => void;
15
15
  }
16
16
 
17
- export const IssuePreview = (props: IssuePreviewProps) => {
17
+ export const PreviewItem = (props: PreviewItemProps) => {
18
18
  const {data, onShowMore} = props;
19
19
 
20
20
  if (!data) {
@@ -27,9 +27,7 @@ export const IssuePreview = (props: IssuePreviewProps) => {
27
27
  <Text as="div" color="secondary" variant="body-2">
28
28
  {data.message}
29
29
  </Text>
30
- <Link onClick={() => onShowMore && onShowMore(data.id)}>
31
- {i18n('label.show-details')}
32
- </Link>
30
+ <Link onClick={() => onShowMore?.(data.id)}>{i18n('label.show-details')}</Link>
33
31
  </div>
34
32
  );
35
33
  };
@@ -0,0 +1 @@
1
+ export * from './PreviewItem';
@@ -15,9 +15,12 @@ import {
15
15
  import {EPathType} from '../../../../types/api/schema';
16
16
  import {isColumnEntityType, isTableType} from '../../utils/schema';
17
17
  //@ts-ignore
18
- import {getSchema} from '../../../../store/reducers/schema';
18
+ import {getSchema, resetLoadingState} from '../../../../store/reducers/schema';
19
19
  //@ts-ignore
20
- import {getOlapStats} from '../../../../store/reducers/olapStats';
20
+ import {
21
+ getOlapStats,
22
+ resetLoadingState as resetOlapLoadingState,
23
+ } from '../../../../store/reducers/olapStats';
21
24
  import {useAutofetcher} from '../../../../utils/hooks';
22
25
 
23
26
  import './Overview.scss';
@@ -64,24 +67,36 @@ function Overview(props: OverviewProps) {
64
67
 
65
68
  const {
66
69
  currentSchema: currentItem = {},
67
- loading,
70
+ loading: schemaLoading,
68
71
  wasLoaded,
69
72
  autorefresh,
70
73
  currentSchemaPath,
71
74
  } = useSelector((state: any) => state.schema);
72
75
 
73
- const {
74
- data: { result: olapStats } = { result: undefined },
75
- } = useSelector((state: any) => state.olapStats);
76
+ const {data: {result: olapStats} = {result: undefined}, loading: olapStatsLoading} =
77
+ useSelector((state: any) => state.olapStats);
78
+
79
+ const loading = schemaLoading || olapStatsLoading;
76
80
 
77
- useAutofetcher(() => {
78
- const schemaPath = currentSchemaPath || tenantName;
79
- dispatch(getSchema({path: schemaPath}));
81
+ useAutofetcher(
82
+ (isBackground) => {
83
+ if (!isBackground) {
84
+ dispatch(resetLoadingState());
85
+ }
80
86
 
81
- if (isTableType(type) && isColumnEntityType(type)) {
82
- dispatch(getOlapStats({path: schemaPath}));
83
- }
84
- }, [currentSchemaPath, dispatch, tenantName, type], autorefresh);
87
+ const schemaPath = currentSchemaPath || tenantName;
88
+ dispatch(getSchema({path: schemaPath}));
89
+
90
+ if (isTableType(type) && isColumnEntityType(type)) {
91
+ if (!isBackground) {
92
+ dispatch(resetOlapLoadingState());
93
+ }
94
+ dispatch(getOlapStats({path: schemaPath}));
95
+ }
96
+ },
97
+ [currentSchemaPath, dispatch, tenantName, type],
98
+ autorefresh,
99
+ );
85
100
 
86
101
  const tableSchema =
87
102
  currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
@@ -116,17 +131,17 @@ function Overview(props: OverviewProps) {
116
131
  [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
117
132
  };
118
133
 
119
- return (type && pathTypeToComponent[type]?.()) || (
120
- <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
134
+ return (
135
+ (type && pathTypeToComponent[type]?.()) || (
136
+ <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
137
+ )
121
138
  );
122
- }
139
+ };
123
140
 
124
141
  return loading && !wasLoaded ? (
125
142
  renderLoader()
126
143
  ) : (
127
- <div className={props.className}>
128
- {renderContent()}
129
- </div>
144
+ <div className={props.className}>{renderContent()}</div>
130
145
  );
131
146
  }
132
147
 
@@ -52,7 +52,13 @@
52
52
  &__result {
53
53
  overflow: auto;
54
54
 
55
+ // This fixes last row display for ordinary preview (not fullscreen)
55
56
  height: calc(100% - 40px);
56
57
  padding: 0 10px;
58
+
59
+ // Fix white space footer block for fullscreen preview
60
+ .kv-fullscreen & {
61
+ height: 100%;
62
+ }
57
63
  }
58
64
  }
@@ -54,8 +54,6 @@ export const RUN_ACTIONS = [
54
54
 
55
55
  const TABLE_SETTINGS = {
56
56
  sortable: false,
57
- dynamicItemSizeGetter: () => 40,
58
- dynamicRenderType: 'variable',
59
57
  };
60
58
 
61
59
  const EDITOR_OPTIONS = {
@@ -511,13 +509,7 @@ function QueryEditor(props) {
511
509
  };
512
510
 
513
511
  const renderControls = () => {
514
- const {
515
- executeQuery,
516
- explainQuery,
517
- savedQueries,
518
- selectRunAction,
519
- setSettingValue,
520
- } = props;
512
+ const {executeQuery, explainQuery, savedQueries, selectRunAction, setSettingValue} = props;
521
513
  const {runAction} = executeQuery;
522
514
  const runIsDisabled = !executeQuery.input || executeQuery.loading;
523
515
  const runText = _.find(RUN_ACTIONS, {value: runAction}).content;
@@ -15,8 +15,8 @@
15
15
  }
16
16
  &_fullscreen {
17
17
  width: 100%;
18
- margin-top: 0;
19
- padding: 10px;
18
+ margin-top: 10px;
19
+ padding: 0 10px 10px;
20
20
  }
21
21
  }
22
22
 
@@ -83,7 +83,7 @@ function SaveQuery({savedQueries, onSaveQuery, saveButtonDisabled}) {
83
83
  <TextInput
84
84
  id="queryName"
85
85
  placeholder="Enter query name"
86
- text={queryName}
86
+ value={queryName}
87
87
  onUpdate={onQueryNameChange}
88
88
  hasClear
89
89
  autoFocus
@@ -3,7 +3,7 @@ import {useDispatch} from 'react-redux';
3
3
 
4
4
  import {NavigationTree} from 'ydb-ui-components';
5
5
 
6
- import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
6
+ import {setCurrentSchemaPath, getSchema, preloadSchema} from '../../../../store/reducers/schema';
7
7
  import {getDescribe} from '../../../../store/reducers/describe';
8
8
  import {getSchemaAcl} from '../../../../store/reducers/schemaAcl';
9
9
  import type {EPathType} from '../../../../types/api/schema';
@@ -19,28 +19,35 @@ interface SchemaTreeProps {
19
19
  }
20
20
 
21
21
  export function SchemaTree(props: SchemaTreeProps) {
22
- const {
23
- rootPath,
24
- rootName,
25
- rootType,
26
- currentPath,
27
- } = props;
22
+ const {rootPath, rootName, rootType, currentPath} = props;
28
23
 
29
24
  const dispatch = useDispatch();
30
25
 
31
- const fetchPath = (path: string) => window.api.getSchema(
32
- {path},
33
- {concurrentId: `NavigationTree.getSchema|${path}`},
34
- )
35
- .then(({PathDescription: {Children = []} = {}}) => {
36
- return Children.map(({Name = '', PathType, PathSubType}) => ({
37
- name: Name,
38
- type: mapPathTypeToNavigationTreeType(PathType, PathSubType),
39
- // FIXME: should only be explicitly set to true for tables with indexes
40
- // at the moment of writing there is no property to determine this, fix later
41
- expandable: true,
42
- }));
43
- });
26
+ const fetchPath = (path: string) =>
27
+ window.api
28
+ .getSchema({path}, {concurrentId: `NavigationTree.getSchema|${path}`})
29
+ .then((data) => {
30
+ const {PathDescription: {Children = []} = {}} = data;
31
+
32
+ dispatch(preloadSchema(path, data));
33
+
34
+ return Children.map((childData) => {
35
+ const {Name = '', PathType, PathSubType} = childData;
36
+
37
+ // not full data, but it contains PathType, which ensures seamless switch between nodes
38
+ dispatch(
39
+ preloadSchema(`${path}/${Name}`, {PathDescription: {Self: childData}}),
40
+ );
41
+
42
+ return {
43
+ name: Name,
44
+ type: mapPathTypeToNavigationTreeType(PathType, PathSubType),
45
+ // FIXME: should only be explicitly set to true for tables with indexes
46
+ // at the moment of writing there is no property to determine this, fix later
47
+ expandable: true,
48
+ };
49
+ });
50
+ });
44
51
 
45
52
  const handleActivePathUpdate = (activePath: string) => {
46
53
  dispatch(setCurrentSchemaPath(activePath));