ydb-embedded-ui 1.4.1 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/components/GroupTreeViewer/GroupTreeViewer.js +3 -2
  3. package/dist/components/GroupTreeViewer/GroupTreeViewer.scss +0 -2
  4. package/dist/components/SplitPane/SplitPane.tsx +8 -8
  5. package/dist/containers/App/App.js +1 -0
  6. package/dist/containers/App/App.scss +0 -26
  7. package/dist/containers/Authentication/Authentication.tsx +1 -0
  8. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssueViewer.scss +11 -23
  9. package/dist/containers/Tenant/Diagnostics/Healthcheck/IssuesViewer/IssuesViewer.js +7 -7
  10. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +9 -15
  11. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +16 -15
  12. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +2 -2
  13. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +36 -54
  14. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.scss +7 -5
  15. package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.js +1 -0
  16. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +64 -0
  17. package/dist/containers/Tenant/Tenant.tsx +2 -2
  18. package/dist/containers/Tenant/utils/schema.ts +17 -0
  19. package/dist/containers/Tenant/utils/schemaActions.ts +130 -0
  20. package/dist/services/api.d.ts +3 -0
  21. package/dist/services/api.js +2 -2
  22. package/package.json +8 -4
  23. package/dist/components/TreeView/TreeView.js +0 -60
  24. package/dist/components/TreeView/TreeView.scss +0 -39
  25. package/dist/containers/Tenant/Schema/SchemaNode/SchemaNode.js +0 -170
  26. package/dist/containers/Tenant/Schema/SchemaNode/SchemaNode.scss +0 -62
  27. package/dist/containers/Tenant/Schema/SchemaNodeActions/SchemaNodeActions.scss +0 -17
  28. package/dist/containers/Tenant/Schema/SchemaNodeActions/SchemaNodeActions.tsx +0 -125
  29. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.js +0 -116
  30. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.scss +0 -17
  31. package/dist/styles/react-treeview.scss +0 -45
@@ -127,7 +127,7 @@ function Tenant(props: TenantProps) {
127
127
  dispatchSummaryVisibilityAction(PaneVisibilityActionTypes.triggerExpand);
128
128
  };
129
129
 
130
- const onSplitStartDrugAdditional = () => {
130
+ const onSplitStartDragAdditional = () => {
131
131
  dispatchSummaryVisibilityAction(PaneVisibilityActionTypes.clear);
132
132
  };
133
133
 
@@ -139,7 +139,7 @@ function Tenant(props: TenantProps) {
139
139
  triggerCollapse={summaryVisibilityState.triggerCollapse}
140
140
  triggerExpand={summaryVisibilityState.triggerExpand}
141
141
  minSize={[36, 200]}
142
- onSplitStartDrugAdditional={onSplitStartDrugAdditional}
142
+ onSplitStartDragAdditional={onSplitStartDragAdditional}
143
143
  >
144
144
  <ObjectSummary
145
145
  type={entityType as string}
@@ -0,0 +1,17 @@
1
+ import type {NavigationTreeNodeType} from "ydb-ui-components";
2
+
3
+ const DB_TYPES = new Set(['EPathTypeSubDomain']);
4
+ const TABLE_TYPES = new Set(['EPathTypeTable', 'EPathTypeOlapTable']);
5
+ const DIR_TYPES = new Set(['EPathTypeDir', 'EPathTypeOlapStore']);
6
+
7
+ export const calcNavigationTreeType = (type: string): NavigationTreeNodeType => {
8
+ if (DIR_TYPES.has(type)) {
9
+ return 'directory';
10
+ } else if (TABLE_TYPES.has(type)) {
11
+ return 'table';
12
+ } else if (DB_TYPES.has(type)) {
13
+ return 'database';
14
+ }
15
+
16
+ return 'directory';
17
+ };
@@ -0,0 +1,130 @@
1
+ import qs from 'qs';
2
+ import {Dispatch} from 'react';
3
+ import {History} from 'history';
4
+ import type {NavigationTreeNodeType} from 'ydb-ui-components';
5
+
6
+ import routes, {createHref} from '../../../routes';
7
+ import {changeUserInput} from '../../../store/reducers/executeQuery';
8
+ import {setShowPreview} from '../../../store/reducers/schema';
9
+ import createToast from '../../../utils/createToast';
10
+ import {TenantGeneralTabsIds, TenantTabsGroups} from '../TenantPages';
11
+
12
+ const createTableTemplate = (path: string) => {
13
+ return `CREATE TABLE \`${path}/my_table\`
14
+ (
15
+ \`id\` Uint64,
16
+ \`name\` String,
17
+ PRIMARY KEY (\`id\`)
18
+ );`;
19
+ };
20
+
21
+ const alterTableTemplate = (path: string) => {
22
+ return `ALTER TABLE \`${path}\`
23
+ ADD COLUMN is_deleted Bool;`;
24
+ };
25
+ const selectQueryTemplate = (path: string) => {
26
+ return `SELECT \`id\`, \`name\`
27
+ FROM \`${path}\`
28
+ ORDER BY \`id\`
29
+ LIMIT 10;`;
30
+ };
31
+ const upsertQueryTemplate = (path: string) => {
32
+ return `UPSERT INTO \`${path}\`
33
+ ( \`id\`, \`name\` )
34
+ VALUES ( );`;
35
+ };
36
+
37
+ export const getActions = (
38
+ dispatch: Dispatch<any>,
39
+ history: History<unknown>,
40
+ setActivePath: (path: string) => void,
41
+ ) =>
42
+ (path: string, type: NavigationTreeNodeType) => {
43
+ const queryParams = qs.parse(location.search, {
44
+ ignoreQueryPrefix: true,
45
+ });
46
+
47
+ const switchTabToQuery = () => {
48
+ history.push(
49
+ createHref(routes.tenant, undefined, {
50
+ ...queryParams,
51
+ [TenantTabsGroups.general]: TenantGeneralTabsIds.query,
52
+ }),
53
+ );
54
+ };
55
+
56
+ const onCreateTableClick = () => {
57
+ dispatch(changeUserInput({input: createTableTemplate(path)}));
58
+ switchTabToQuery();
59
+ // here and in the other handlers this should be called after switching tab:
60
+ // redux-location-state catches the history.push event from the tab switching
61
+ // before active path updates in url, preventing its update at all
62
+ setActivePath(path);
63
+ };
64
+
65
+ const onAlterTableClick = () => {
66
+ dispatch(changeUserInput({input: alterTableTemplate(path)}));
67
+ switchTabToQuery();
68
+ setActivePath(path);
69
+ };
70
+
71
+ const onSelectQueryClick = () => {
72
+ dispatch(changeUserInput({input: selectQueryTemplate(path)}));
73
+ switchTabToQuery();
74
+ setActivePath(path);
75
+ };
76
+
77
+ const onUpsertQueryClick = () => {
78
+ dispatch(changeUserInput({input: upsertQueryTemplate(path)}));
79
+ switchTabToQuery();
80
+ setActivePath(path);
81
+ };
82
+
83
+ const onCopyPathClick = () => {
84
+ navigator.clipboard
85
+ .writeText(path)
86
+ .then(() => {
87
+ createToast({
88
+ name: 'Copied',
89
+ title: 'The path is copied to the clipboard',
90
+ type: 'success',
91
+ });
92
+ })
93
+ .catch(() => {
94
+ createToast({
95
+ name: 'Not copied',
96
+ title: 'Couldn’t copy the path',
97
+ type: 'error',
98
+ });
99
+ });
100
+ };
101
+
102
+ const onOpenPreviewClick = () => {
103
+ dispatch(setShowPreview(true));
104
+ switchTabToQuery();
105
+ setActivePath(path);
106
+ };
107
+
108
+ const copyItem = {text: 'Copy path', action: onCopyPathClick};
109
+
110
+ return type === 'table'
111
+ ? [
112
+ [
113
+ {text: 'Open preview', action: onOpenPreviewClick},
114
+ copyItem,
115
+ ],
116
+ [
117
+ {text: 'Alter table...', action: onAlterTableClick},
118
+ {text: 'Select query...', action: onSelectQueryClick},
119
+ {text: 'Upsert query...', action: onUpsertQueryClick},
120
+ ],
121
+ ]
122
+ : [
123
+ [
124
+ copyItem,
125
+ ],
126
+ [
127
+ {text: 'Create table...', action: onCreateTableClick},
128
+ ],
129
+ ];
130
+ };
@@ -0,0 +1,3 @@
1
+ interface Window {
2
+ api: any;
3
+ }
@@ -76,7 +76,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
76
76
  enums: true,
77
77
  });
78
78
  }
79
- getSchema({path}) {
79
+ getSchema({path}, {concurrentId} = {}) {
80
80
  return this.get(
81
81
  this.getPath('/viewer/json/describe'),
82
82
  {
@@ -88,7 +88,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
88
88
  partition_stats: false,
89
89
  partitioning_info: false,
90
90
  },
91
- {concurrentId: `getSchema|${path}`},
91
+ {concurrentId: concurrentId || `getSchema|${path}`},
92
92
  );
93
93
  }
94
94
  getDescribe({path}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "1.4.1",
3
+ "version": "1.5.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -13,7 +13,7 @@
13
13
  "@testing-library/react": "11.2.7",
14
14
  "@testing-library/user-event": "12.8.3",
15
15
  "@types/qs": "6.9.7",
16
- "@yandex-cloud/i18n": "0.2.0",
16
+ "@yandex-cloud/i18n": "0.4.0",
17
17
  "@yandex-cloud/paranoid": "1.0.0",
18
18
  "@yandex-cloud/react-data-table": "0.2.1",
19
19
  "axios": "0.19.2",
@@ -34,13 +34,13 @@
34
34
  "react-scripts": "4.0.3",
35
35
  "react-split": "2.0.14",
36
36
  "react-transition-group": "4.4.2",
37
- "react-treeview": "0.4.7",
38
37
  "redux": "4.0.1",
39
38
  "redux-location-state": "2.6.0",
40
39
  "redux-thunk": "2.3.0",
41
40
  "reselect": "4.0.0",
42
41
  "sass": "1.32.8",
43
- "web-vitals": "1.1.2"
42
+ "web-vitals": "1.1.2",
43
+ "ydb-ui-components": "1.2.3"
44
44
  },
45
45
  "scripts": {
46
46
  "start": "react-app-rewired start",
@@ -88,6 +88,7 @@
88
88
  "@types/react-transition-group": "^4.4.4",
89
89
  "@types/react-virtualized-auto-sizer": "^1.0.1",
90
90
  "@yandex-cloud/axios-wrapper": "^1.0.2",
91
+ "@yandex-cloud/browserslist-config": "1.0.1",
91
92
  "@yandex-cloud/eslint-config": "^1.0.0",
92
93
  "@yandex-cloud/prettier-config": "^1.0.0",
93
94
  "@yandex-cloud/stylelint-config": "^1.1.0",
@@ -104,5 +105,8 @@
104
105
  "react-dom": "^17.0.2",
105
106
  "stylelint": "^14.3.0",
106
107
  "typescript": "^4.5.5"
108
+ },
109
+ "peerDependencies": {
110
+ "@yandex-cloud/browserslist-config": "^1.0.1"
107
111
  }
108
112
  }
@@ -1,60 +0,0 @@
1
- import PropTypes from 'prop-types';
2
- import cn from 'bem-cn-lite';
3
- import TreeViewBase from 'react-treeview';
4
- import Icon from '../Icon/Icon';
5
-
6
- import './TreeView.scss';
7
-
8
- const b = cn('km-tree-view');
9
-
10
- const TreeView = (props) => {
11
- const {
12
- children,
13
- nodeLabel,
14
- onClick,
15
- collapsed,
16
- clickableLabel = false,
17
- className,
18
- hasArrow = true,
19
- ...rest
20
- } = props;
21
-
22
- const newNodeLabel = (
23
- <div
24
- className={b('node-wrapper', {clickable: clickableLabel})}
25
- onClick={clickableLabel ? onClick : undefined}
26
- >
27
- {hasArrow ? (
28
- <span
29
- className={b('arrow-icon', {extended: !collapsed})}
30
- onClick={clickableLabel ? undefined : onClick}
31
- >
32
- <Icon name="arrow-right" viewBox="0 0 6 11" width={6} height={11} />
33
- </span>
34
- ) : null}
35
- {nodeLabel}
36
- </div>
37
- );
38
-
39
- return (
40
- <TreeViewBase
41
- {...rest}
42
- treeViewClassName={b(null, className)}
43
- nodeLabel={newNodeLabel}
44
- collapsed={collapsed}
45
- >
46
- {children}
47
- </TreeViewBase>
48
- );
49
- };
50
-
51
- TreeView.propTypes = {
52
- children: PropTypes.any,
53
- nodeLabel: PropTypes.node,
54
- onClick: PropTypes.func,
55
- collapsed: PropTypes.bool,
56
- clickableLabel: PropTypes.bool,
57
- className: PropTypes.string,
58
- };
59
-
60
- export default TreeView;
@@ -1,39 +0,0 @@
1
- .km-tree-view {
2
- & .tree-view_arrow {
3
- display: none;
4
- }
5
-
6
- &__node-wrapper {
7
- display: flex;
8
- overflow: hidden;
9
- align-items: center;
10
-
11
- width: 100%;
12
-
13
- &_clickable {
14
- cursor: pointer;
15
- }
16
- }
17
-
18
- &__arrow-icon {
19
- display: flex;
20
- justify-content: center;
21
-
22
- width: 11px;
23
- margin-right: 8px;
24
-
25
- cursor: pointer;
26
-
27
- &_extended {
28
- transform: rotate(90deg);
29
- }
30
-
31
- & .yc-icon {
32
- vertical-align: middle;
33
- }
34
- }
35
- .yc-icon {
36
- display: flex;
37
- flex-shrink: 0;
38
- }
39
- }
@@ -1,170 +0,0 @@
1
- import React from 'react';
2
- import {connect} from 'react-redux';
3
- import PropTypes from 'prop-types';
4
- import cn from 'bem-cn-lite';
5
-
6
- import TreeView from '../../../../components/TreeView/TreeView';
7
- import SchemaTree from '../../../Tenant/Schema/SchemaTree/SchemaTree';
8
- import Icon from '../../../../components/Icon/Icon';
9
-
10
- import {getSchema, setCurrentSchemaPath} from '../../../../store/reducers/schema';
11
- import {getDescribe} from '../../../../store/reducers/describe';
12
- import {getSchemaAcl} from '../../../../store/reducers/schemaAcl';
13
-
14
- import './SchemaNode.scss';
15
- import SchemaNodeActions from '../SchemaNodeActions/SchemaNodeActions';
16
- import {isTableType} from '../../Tenant';
17
-
18
- const b = cn('schema-node');
19
-
20
- export const SUBDOMAIN_FOLDER_TYPE = 'EPathTypeSubDomain';
21
- export const TABLE_TYPE = 'EPathTypeTable';
22
- export const OLAP_TABLE_TYPE = 'EPathTypeOlapTable';
23
-
24
- export const FOLDERS_TYPE = ['EPathTypeDir', 'EPathTypeExtSubDomain', 'EPathTypeOlapStore'];
25
-
26
- class SchemaNode extends React.Component {
27
- static propTypes = {
28
- data: PropTypes.object.isRequired,
29
- fullPath: PropTypes.string.isRequired,
30
- getSchema: PropTypes.func.isRequired,
31
- setCurrentSchemaPath: PropTypes.func,
32
- currentSchemaPath: PropTypes.string,
33
- isRoot: PropTypes.bool,
34
- };
35
-
36
- state = {
37
- collapsed: true,
38
- active: false,
39
- };
40
-
41
- schemaNodeRef = React.createRef();
42
-
43
- componentDidMount() {
44
- const {currentSchemaPath, isRoot} = this.props;
45
- const schemaPath = this.getSchemaPath();
46
-
47
- if (schemaPath === currentSchemaPath && !this.state.active) {
48
- this.addActiveClass();
49
- }
50
-
51
- if (
52
- (currentSchemaPath &&
53
- currentSchemaPath.startsWith(schemaPath) &&
54
- currentSchemaPath !== schemaPath) ||
55
- isRoot
56
- ) {
57
- this.setState({collapsed: false});
58
- }
59
- }
60
-
61
- componentDidUpdate() {
62
- const {currentSchemaPath} = this.props;
63
- const schemaPath = this.getSchemaPath();
64
-
65
- if (schemaPath === currentSchemaPath && !this.state.active) {
66
- this.addActiveClass();
67
- }
68
- }
69
-
70
- getSchemaPath = () => {
71
- const {data, fullPath, isRoot} = this.props;
72
-
73
- return isRoot ? fullPath : `${fullPath}/${data.Name}`;
74
- };
75
-
76
- invertCollapsed = () => {
77
- this.setState({collapsed: !this.state.collapsed});
78
- };
79
-
80
- setIcon = (data) => {
81
- const viewBox = '0 0 16 16';
82
- const {collapsed} = this.state;
83
- if (FOLDERS_TYPE.indexOf(data.PathType) !== -1) {
84
- return collapsed ? (
85
- <Icon name="folder" viewBox={viewBox} width={16} height={16} />
86
- ) : (
87
- <Icon name="openFolder" viewBox={viewBox} width={16} height={16} />
88
- );
89
- } else if (data.PathType === TABLE_TYPE || data.PathType === OLAP_TABLE_TYPE) {
90
- return <Icon name="table" viewBox={viewBox} width={16} height={16} />;
91
- } else if (data.PathType === SUBDOMAIN_FOLDER_TYPE) {
92
- return <Icon name="subdomain" viewBox={viewBox} width={16} height={16} />;
93
- }
94
- };
95
-
96
- addActiveClass = () => {
97
- const activeClass = 'schema-node_active';
98
- const currentActiveSchemaNode = document.querySelector(`.${activeClass}`);
99
- if (currentActiveSchemaNode) {
100
- currentActiveSchemaNode.classList.remove(activeClass);
101
- }
102
- const activeNode = this.schemaNodeRef.current;
103
- if (activeNode) {
104
- this.setState({active: true});
105
- activeNode.classList.add(activeClass);
106
- }
107
- };
108
-
109
- handleClick = (e) => {
110
- const {getSchema, getDescribe, getSchemaAcl, setCurrentSchemaPath} = this.props;
111
- e.stopPropagation();
112
- this.addActiveClass();
113
-
114
- const schemaPath = this.getSchemaPath();
115
- setCurrentSchemaPath(schemaPath);
116
- getSchema({path: schemaPath});
117
- getDescribe({path: schemaPath});
118
- getSchemaAcl({path: schemaPath});
119
- };
120
-
121
- render() {
122
- const {data, fullPath, isRoot = false, currentSchemaPath, currentItem = {}} = this.props;
123
- const {collapsed} = this.state;
124
-
125
- if (!data) {
126
- return null;
127
- }
128
- const currentPathType = currentItem.PathDescription?.Self?.PathType;
129
- const type = isTableType(currentPathType);
130
-
131
- const hasArrow = data.PathType !== TABLE_TYPE;
132
- const label = (
133
- <div className={b('label')}>
134
- {this.setIcon(data)}
135
- <div className={b('name-wrapper')}>
136
- <div className={b('name')}>{data.Name}</div>
137
- <SchemaNodeActions name={currentSchemaPath} isTableType={type} />
138
- </div>
139
- </div>
140
- );
141
- return (
142
- <div onClick={this.handleClick} ref={this.schemaNodeRef}>
143
- <TreeView
144
- nodeLabel={label}
145
- collapsed={collapsed}
146
- onClick={this.invertCollapsed}
147
- hasArrow={hasArrow}
148
- >
149
- <SchemaTree path={isRoot ? fullPath : `${fullPath}/${data.Name}`} />
150
- </TreeView>
151
- </div>
152
- );
153
- }
154
- }
155
-
156
- function mapStateToProps(state) {
157
- return {
158
- currentSchemaPath: state.schema.currentSchemaPath,
159
- currentItem: state.schema.currentSchema,
160
- };
161
- }
162
-
163
- const mapDispatchToProps = {
164
- getSchema,
165
- getDescribe,
166
- getSchemaAcl,
167
- setCurrentSchemaPath,
168
- };
169
-
170
- export default connect(mapStateToProps, mapDispatchToProps)(SchemaNode);
@@ -1,62 +0,0 @@
1
- .schema-node {
2
- display: flex;
3
- align-items: center;
4
-
5
- .tree-view_item {
6
- flex-wrap: wrap;
7
-
8
- margin: 0;
9
- padding: 5px 0;
10
- }
11
-
12
- .tree-view_children {
13
- margin: 0;
14
- padding-left: 15px;
15
- }
16
-
17
- &__label {
18
- display: flex;
19
- overflow: hidden;
20
- align-items: center;
21
-
22
- width: 100%;
23
- height: 20px;
24
- padding: 0 5px;
25
-
26
- cursor: pointer;
27
-
28
- & > svg {
29
- color: var(--yc-color-text-hint);
30
- }
31
- }
32
-
33
- &_active > .tree-view > .tree-view_item {
34
- font-weight: 600;
35
-
36
- background-color: var(--yc-color-base-info);
37
- }
38
-
39
- &__name-wrapper {
40
- display: inline-flex;
41
- justify-content: space-between;
42
-
43
- width: 100%;
44
- }
45
-
46
- &__name {
47
- display: inline;
48
- overflow: hidden;
49
-
50
- margin-left: 6px;
51
-
52
- white-space: nowrap;
53
- text-overflow: ellipsis;
54
- }
55
- }
56
-
57
- .tree-view_item {
58
- border-bottom: 1px solid var(--yc-color-line-generic);
59
- &:hover {
60
- background-color: var(--yc-color-base-simple-hover);
61
- }
62
- }
@@ -1,17 +0,0 @@
1
- .kv-schema-node-actions {
2
- visibility: hidden;
3
-
4
- &__popup {
5
- .yc-menu__list-item {
6
- line-height: 36px;
7
- }
8
- }
9
- }
10
-
11
- .tree-view_item {
12
- &:hover {
13
- .kv-schema-node-actions {
14
- visibility: visible;
15
- }
16
- }
17
- }