ydb-embedded-ui 1.1.1 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.3...v1.2.0) (2022-04-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Storage:** smoother loading state for storage table ([f7f38c4](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/f7f38c455dd9abc3f898048081e90af9b460f922))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * prevent ghost autofetch ([153d829](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/153d8291d315f1dab001a69981a12e30d3d2aea9))
14
+
15
+ ### [1.1.3](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.2...v1.1.3) (2022-04-20)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * should prepare internal link correctly ([3da36e2](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/3da36e22f6adbce6a1b14ac1afb0fb4aa46bb75f))
21
+
22
+ ### [1.1.2](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.1...v1.1.2) (2022-04-19)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **ObjectSummary:** should correctly parse table creation time ([c9887dd](https://www.github.com/ydb-platform/ydb-embedded-ui/commit/c9887dd162720667dcbe3b4834b3b0ba5a9f3f6e))
28
+
3
29
  ### [1.1.1](https://www.github.com/ydb-platform/ydb-embedded-ui/compare/v1.1.0...v1.1.1) (2022-04-19)
4
30
 
5
31
 
@@ -32,7 +32,7 @@ class FullNodeViewer extends React.Component {
32
32
  render() {
33
33
  const {node, className, additionalNodesInfo={}} = this.props;
34
34
  const nodeHref = additionalNodesInfo.getNodeRef
35
- ? additionalNodesInfo.getNodeRef(node)
35
+ ? additionalNodesInfo.getNodeRef(node) + 'internal'
36
36
  : undefined;
37
37
 
38
38
  const commonInfo = [
@@ -0,0 +1,38 @@
1
+ .table-skeleton {
2
+ width: 100%;
3
+
4
+ &__row {
5
+ display: flex;
6
+ align-items: center;
7
+
8
+ height: var(--data-table-row-height);
9
+
10
+ .yc-skeleton {
11
+ height: var(--yc-text-body2-line-height);
12
+ }
13
+ }
14
+
15
+ &__col-1 {
16
+ width: 10%;
17
+ margin-right: 5%;
18
+ }
19
+
20
+ &__col-2 {
21
+ width: 7%;
22
+ margin-right: 5%;
23
+ }
24
+
25
+ &__col-3,
26
+ &__col-4 {
27
+ width: 5%;
28
+ margin-right: 5%;
29
+ }
30
+
31
+ &__col-5 {
32
+ width: 20%;
33
+ }
34
+
35
+ &__col-full {
36
+ width: 100%;
37
+ }
38
+ }
@@ -0,0 +1,29 @@
1
+ import { FC } from 'react';
2
+ import block from 'bem-cn-lite';
3
+ import { Skeleton } from '@yandex-cloud/uikit';
4
+
5
+ import './TableSkeleton.scss';
6
+
7
+ const b = block('table-skeleton');
8
+
9
+ interface TableSkeletonProps {
10
+ className?: string;
11
+ rows?: number;
12
+ }
13
+
14
+ export const TableSkeleton: FC<TableSkeletonProps> = ({ rows = 2, className }) => (
15
+ <div className={b(null, className)}>
16
+ <div className={b('row')}>
17
+ <Skeleton className={b('col-1')} />
18
+ <Skeleton className={b('col-2')} />
19
+ <Skeleton className={b('col-3')} />
20
+ <Skeleton className={b('col-4')} />
21
+ <Skeleton className={b('col-5')} />
22
+ </div>
23
+ {[...new Array(rows)].map((_, index) => (
24
+ <div className={b('row')} key={`skeleton-row-${index}`}>
25
+ <Skeleton className={b('col-full')} />
26
+ </div>
27
+ ))}
28
+ </div>
29
+ );
@@ -68,11 +68,11 @@ function getColumns({
68
68
  header: vDiskTableColumnsNames[VDiskTableColumnsIds.slotId],
69
69
  width: 100,
70
70
  render: ({value, row}) => {
71
- let vdiskInternalViewerLink: string | undefined;
71
+ let vdiskInternalViewerLink = '';
72
72
 
73
73
  if (nodeHref && value !== undefined) {
74
74
  vdiskInternalViewerLink +=
75
- nodeHref + '/actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(value);
75
+ nodeHref + 'actors/vdisks/vdisk' + pad9(pDiskId) + '_' + pad9(value);
76
76
  }
77
77
 
78
78
  return (
@@ -195,10 +195,10 @@ export function PDisk(props: PDiskProps) {
195
195
  SerialNumber,
196
196
  } = data;
197
197
 
198
- let pDiskInternalViewerLink: string | undefined;
198
+ let pDiskInternalViewerLink = '';
199
199
 
200
200
  if (nodeHref) {
201
- pDiskInternalViewerLink += nodeHref + '/actors/pdisks/pdisk' + pad9(PDiskId);
201
+ pDiskInternalViewerLink += nodeHref + 'actors/pdisks/pdisk' + pad9(PDiskId);
202
202
  }
203
203
 
204
204
  const pdiskInfo: any = [
@@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
3
3
  import {connect} from 'react-redux';
4
4
  import cn from 'bem-cn-lite';
5
5
  import DataTable from '@yandex-cloud/react-data-table';
6
- import {Loader, RadioButton, Label} from '@yandex-cloud/uikit';
6
+ import {RadioButton, Label} from '@yandex-cloud/uikit';
7
7
 
8
8
  import StorageFilter from './StorageFilter/StorageFilter';
9
9
  import {AutoFetcher} from '../../utils/autofetcher';
10
+ import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
10
11
 
11
12
  import {
12
13
  getStorageInfo,
@@ -68,17 +69,14 @@ class Storage extends React.Component {
68
69
  storageType,
69
70
  setHeader,
70
71
  getNodesList,
71
- getStorageInfo,
72
72
  } = this.props;
73
73
 
74
74
  this.autofetcher = new AutoFetcher();
75
75
  getNodesList();
76
76
  if (tenant || nodeId) {
77
77
  setVisibleEntities(VisibleEntities.All);
78
- getStorageInfo({
79
- tenant,
78
+ this.getStorageInfo({
80
79
  filter: FILTER_OPTIONS.All,
81
- nodeId,
82
80
  type: storageType,
83
81
  });
84
82
  } else {
@@ -88,16 +86,12 @@ class Storage extends React.Component {
88
86
  link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.storage.id}),
89
87
  },
90
88
  ]);
91
- getStorageInfo({
92
- tenant,
93
- nodeId,
89
+ this.getStorageInfo({
94
90
  filter: FILTER_OPTIONS.Missing,
95
91
  type: storageType,
96
92
  });
97
93
  this.autofetcher.fetch(() =>
98
- getStorageInfo({
99
- tenant,
100
- nodeId,
94
+ this.getStorageInfo({
101
95
  filter: FILTER_OPTIONS.Missing,
102
96
  type: storageType,
103
97
  }),
@@ -107,30 +101,23 @@ class Storage extends React.Component {
107
101
 
108
102
  componentDidUpdate(prevProps) {
109
103
  const {
110
- tenant,
111
104
  visibleEntities,
112
- getStorageInfo,
113
- nodeId,
114
105
  storageType,
115
106
  autorefresh,
116
107
  database,
117
108
  } = this.props;
118
109
 
119
110
  const startFetch = () => {
120
- getStorageInfo({
121
- tenant,
111
+ this.getStorageInfo({
122
112
  filter: FILTER_OPTIONS[visibleEntities],
123
- nodeId,
124
113
  type: storageType,
125
114
  });
126
115
 
127
116
  this.autofetcher.stop();
128
117
  this.autofetcher.start();
129
118
  this.autofetcher.fetch(() =>
130
- getStorageInfo({
131
- tenant,
119
+ this.getStorageInfo({
132
120
  filter: FILTER_OPTIONS[visibleEntities],
133
- nodeId,
134
121
  type: storageType,
135
122
  }),
136
123
  );
@@ -157,11 +144,25 @@ class Storage extends React.Component {
157
144
  this.props.setInitialState();
158
145
  }
159
146
 
147
+ getStorageInfo(data) {
148
+ const {
149
+ tenant,
150
+ nodeId,
151
+ getStorageInfo,
152
+ } = this.props;
153
+
154
+ getStorageInfo({
155
+ tenant,
156
+ nodeId,
157
+ ...data,
158
+ }, {
159
+ concurrentId: 'getStorageInfo',
160
+ });
161
+ }
162
+
160
163
  renderLoader() {
161
164
  return (
162
- <div className={b('loader')}>
163
- <Loader size="m" />
164
- </div>
165
+ <TableSkeleton className={b('loader')}/>
165
166
  );
166
167
  }
167
168
 
@@ -200,8 +201,9 @@ class Storage extends React.Component {
200
201
  };
201
202
 
202
203
  renderControls() {
203
- const {setStorageFilter, visibleEntities, storageType, flatListStorageEntities} =
204
+ const {setStorageFilter, visibleEntities, storageType, flatListStorageEntities, loading, wasLoaded} =
204
205
  this.props;
206
+ const showLoader = loading && !wasLoaded;
205
207
  return (
206
208
  <div className={b('controls')}>
207
209
  <div className={b('search')}>
@@ -232,25 +234,28 @@ class Storage extends React.Component {
232
234
  </RadioButton>
233
235
  <Label theme="info" size="m">{`${
234
236
  storageType === StorageTypes.groups ? 'Groups' : 'Nodes'
235
- }: ${flatListStorageEntities.length}`}</Label>
237
+ }: ${(showLoader) ? '...' : flatListStorageEntities.length}`}</Label>
236
238
  </div>
237
239
  );
238
240
  }
239
241
 
240
242
  render() {
241
243
  const {loading, wasLoaded, error} = this.props;
242
- if (loading && !wasLoaded) {
243
- return this.renderLoader();
244
- } else if (error) {
245
- return <div>{error.statusText}</div>;
246
- } else {
247
- return (
248
- <div className={b()}>
249
- {this.renderControls()}
250
- {this.renderDataTable()}
251
- </div>
252
- );
253
- }
244
+ const showLoader = loading && !wasLoaded;
245
+
246
+ return (
247
+ <div className={b()}>
248
+ {this.renderControls()}
249
+ {error && (
250
+ <div>{error.statusText}</div>
251
+ )}
252
+ {showLoader ? (
253
+ this.renderLoader()
254
+ ) : (
255
+ this.renderDataTable()
256
+ )}
257
+ </div>
258
+ );
254
259
  }
255
260
  }
256
261
 
@@ -43,12 +43,6 @@
43
43
  }
44
44
  }
45
45
 
46
- &__loader {
47
- display: flex;
48
- justify-content: center;
49
-
50
- margin-top: 100px;
51
- }
52
46
  .entity-status {
53
47
  justify-content: center;
54
48
  }
@@ -153,7 +153,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
153
153
  };
154
154
 
155
155
  const renderObjectOverview = () => {
156
- const startTimeInMilliseconds = currentSchemaData?.CreateStep / 1000;
156
+ const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
157
157
  let createTime = '';
158
158
  if (startTimeInMilliseconds) {
159
159
  createTime = new Date(startTimeInMilliseconds).toUTCString();
@@ -35,7 +35,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
35
35
  storage: true,
36
36
  });
37
37
  }
38
- getStorageInfo({tenant, filter, nodeId, type}) {
38
+ getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}) {
39
39
  return this.get(
40
40
  this.getPath(
41
41
  `/viewer/json/${type === StorageTypes.nodes ? 'nodes' : 'storage'}?enums=true`,
@@ -45,6 +45,9 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
45
45
  node_id: nodeId,
46
46
  with: filter,
47
47
  },
48
+ {
49
+ concurrentId,
50
+ },
48
51
  );
49
52
  }
50
53
  getPdiskInfo(nodeId, pdiskId) {
@@ -74,6 +74,8 @@ const storage = function z(state = initialState, action) {
74
74
  return {
75
75
  ...state,
76
76
  visible: action.data,
77
+ wasLoaded: false,
78
+ error: undefined,
77
79
  };
78
80
  }
79
81
  case SET_STORAGE_TYPE: {
@@ -81,6 +83,7 @@ const storage = function z(state = initialState, action) {
81
83
  ...state,
82
84
  type: action.data,
83
85
  wasLoaded: false,
86
+ error: undefined,
84
87
  };
85
88
  }
86
89
  default:
@@ -94,9 +97,9 @@ export function setInitialState() {
94
97
  };
95
98
  }
96
99
 
97
- export function getStorageInfo({tenant, filter, nodeId, type}) {
100
+ export function getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}) {
98
101
  return createApiRequest({
99
- request: window.api.getStorageInfo({tenant, filter, nodeId, type}),
102
+ request: window.api.getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}),
100
103
  actions: FETCH_STORAGE,
101
104
  });
102
105
  }
@@ -3,6 +3,7 @@ export class AutoFetcher {
3
3
  this.timeout = AutoFetcher.DEFAULT_TIMEOUT;
4
4
  this.active = true;
5
5
  this.timer = undefined;
6
+ this.launchCounter = 0;
6
7
  }
7
8
 
8
9
  wait(ms: number) {
@@ -16,6 +17,8 @@ export class AutoFetcher {
16
17
  return;
17
18
  }
18
19
 
20
+ const currentLaunch = this.launchCounter;
21
+
19
22
  await this.wait(this.timeout);
20
23
 
21
24
  if (this.active) {
@@ -23,6 +26,12 @@ export class AutoFetcher {
23
26
  await request();
24
27
  const finishTs = Date.now();
25
28
 
29
+ if (currentLaunch !== this.launchCounter) {
30
+ // autofetcher was restarted while request was in progress
31
+ // stop further fetches, we are in deprecated thread
32
+ return;
33
+ }
34
+
26
35
  const responseTime = finishTs - startTs;
27
36
  const nextTimeout =
28
37
  responseTime > AutoFetcher.MIN_TIMEOUT ? responseTime : AutoFetcher.MIN_TIMEOUT;
@@ -40,6 +49,7 @@ export class AutoFetcher {
40
49
  this.active = false;
41
50
  }
42
51
  start() {
52
+ this.launchCounter++;
43
53
  this.active = true;
44
54
  }
45
55
 
@@ -48,4 +58,5 @@ export class AutoFetcher {
48
58
  timeout: number;
49
59
  active: boolean;
50
60
  timer: undefined | ReturnType<typeof setTimeout>;
61
+ launchCounter: number;
51
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],