Skip to content

Commit f7f38c4

Browse files
committed
feat(Storage): smoother loading state for storage table
1 parent 810ee4e commit f7f38c4

File tree

6 files changed

+118
-46
lines changed

6 files changed

+118
-46
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
);

src/containers/Storage/Storage.js

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
33
import {connect} from 'react-redux';
44
import cn from 'bem-cn-lite';
55
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';
77

88
import StorageFilter from './StorageFilter/StorageFilter';
99
import {AutoFetcher} from '../../utils/autofetcher';
10+
import {TableSkeleton} from '../../components/TableSkeleton/TableSkeleton';
1011

1112
import {
1213
getStorageInfo,
@@ -68,17 +69,14 @@ class Storage extends React.Component {
6869
storageType,
6970
setHeader,
7071
getNodesList,
71-
getStorageInfo,
7272
} = this.props;
7373

7474
this.autofetcher = new AutoFetcher();
7575
getNodesList();
7676
if (tenant || nodeId) {
7777
setVisibleEntities(VisibleEntities.All);
78-
getStorageInfo({
79-
tenant,
78+
this.getStorageInfo({
8079
filter: FILTER_OPTIONS.All,
81-
nodeId,
8280
type: storageType,
8381
});
8482
} else {
@@ -88,16 +86,12 @@ class Storage extends React.Component {
8886
link: createHref(routes.cluster, {activeTab: CLUSTER_PAGES.storage.id}),
8987
},
9088
]);
91-
getStorageInfo({
92-
tenant,
93-
nodeId,
89+
this.getStorageInfo({
9490
filter: FILTER_OPTIONS.Missing,
9591
type: storageType,
9692
});
9793
this.autofetcher.fetch(() =>
98-
getStorageInfo({
99-
tenant,
100-
nodeId,
94+
this.getStorageInfo({
10195
filter: FILTER_OPTIONS.Missing,
10296
type: storageType,
10397
}),
@@ -107,30 +101,23 @@ class Storage extends React.Component {
107101

108102
componentDidUpdate(prevProps) {
109103
const {
110-
tenant,
111104
visibleEntities,
112-
getStorageInfo,
113-
nodeId,
114105
storageType,
115106
autorefresh,
116107
database,
117108
} = this.props;
118109

119110
const startFetch = () => {
120-
getStorageInfo({
121-
tenant,
111+
this.getStorageInfo({
122112
filter: FILTER_OPTIONS[visibleEntities],
123-
nodeId,
124113
type: storageType,
125114
});
126115

127116
this.autofetcher.stop();
128117
this.autofetcher.start();
129118
this.autofetcher.fetch(() =>
130-
getStorageInfo({
131-
tenant,
119+
this.getStorageInfo({
132120
filter: FILTER_OPTIONS[visibleEntities],
133-
nodeId,
134121
type: storageType,
135122
}),
136123
);
@@ -157,11 +144,25 @@ class Storage extends React.Component {
157144
this.props.setInitialState();
158145
}
159146

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+
160163
renderLoader() {
161164
return (
162-
<div className={b('loader')}>
163-
<Loader size="m" />
164-
</div>
165+
<TableSkeleton className={b('loader')}/>
165166
);
166167
}
167168

@@ -200,8 +201,9 @@ class Storage extends React.Component {
200201
};
201202

202203
renderControls() {
203-
const {setStorageFilter, visibleEntities, storageType, flatListStorageEntities} =
204+
const {setStorageFilter, visibleEntities, storageType, flatListStorageEntities, loading, wasLoaded} =
204205
this.props;
206+
const showLoader = loading && !wasLoaded;
205207
return (
206208
<div className={b('controls')}>
207209
<div className={b('search')}>
@@ -232,25 +234,28 @@ class Storage extends React.Component {
232234
</RadioButton>
233235
<Label theme="info" size="m">{`${
234236
storageType === StorageTypes.groups ? 'Groups' : 'Nodes'
235-
}: ${flatListStorageEntities.length}`}</Label>
237+
}: ${(showLoader) ? '...' : flatListStorageEntities.length}`}</Label>
236238
</div>
237239
);
238240
}
239241

240242
render() {
241243
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+
);
254259
}
255260
}
256261

src/containers/Storage/Storage.scss

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,6 @@
4343
}
4444
}
4545

46-
&__loader {
47-
display: flex;
48-
justify-content: center;
49-
50-
margin-top: 100px;
51-
}
5246
.entity-status {
5347
justify-content: center;
5448
}

src/services/api.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
3535
storage: true,
3636
});
3737
}
38-
getStorageInfo({tenant, filter, nodeId, type}) {
38+
getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}) {
3939
return this.get(
4040
this.getPath(
4141
`/viewer/json/${type === StorageTypes.nodes ? 'nodes' : 'storage'}?enums=true`,
@@ -45,6 +45,9 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
4545
node_id: nodeId,
4646
with: filter,
4747
},
48+
{
49+
concurrentId,
50+
},
4851
);
4952
}
5053
getPdiskInfo(nodeId, pdiskId) {

src/store/reducers/storage.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,16 @@ const storage = function z(state = initialState, action) {
7474
return {
7575
...state,
7676
visible: action.data,
77+
wasLoaded: false,
78+
error: undefined,
7779
};
7880
}
7981
case SET_STORAGE_TYPE: {
8082
return {
8183
...state,
8284
type: action.data,
8385
wasLoaded: false,
86+
error: undefined,
8487
};
8588
}
8689
default:
@@ -94,9 +97,9 @@ export function setInitialState() {
9497
};
9598
}
9699

97-
export function getStorageInfo({tenant, filter, nodeId, type}) {
100+
export function getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}) {
98101
return createApiRequest({
99-
request: window.api.getStorageInfo({tenant, filter, nodeId, type}),
102+
request: window.api.getStorageInfo({tenant, filter, nodeId, type}, {concurrentId}),
100103
actions: FETCH_STORAGE,
101104
});
102105
}

0 commit comments

Comments
 (0)