ydb-embedded-ui 4.27.1 → 4.28.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 (42) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/components/EmptyState/EmptyState.scss +5 -2
  3. package/dist/components/EmptyState/EmptyState.tsx +11 -3
  4. package/dist/components/Errors/403/AccessDenied.tsx +4 -3
  5. package/dist/components/ProblemFilter/ProblemFilter.tsx +1 -1
  6. package/dist/components/UptimeFIlter/UptimeFilter.tsx +1 -1
  7. package/dist/components/VirtualTable/VirtualTable.scss +1 -1
  8. package/dist/containers/Cluster/Cluster.tsx +11 -12
  9. package/dist/containers/Nodes/Nodes.tsx +4 -4
  10. package/dist/containers/Nodes/NodesWrapper.tsx +21 -0
  11. package/dist/containers/Nodes/VirtualNodes.tsx +4 -14
  12. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +1 -0
  13. package/dist/containers/Storage/EmptyFilter/EmptyFilter.tsx +1 -0
  14. package/dist/containers/Storage/PDisk/PDisk.tsx +5 -7
  15. package/dist/containers/Storage/Storage.tsx +30 -67
  16. package/dist/containers/Storage/StorageControls/StorageControls.tsx +104 -0
  17. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +18 -62
  18. package/dist/containers/Storage/StorageGroups/StorageGroupsEmptyDataMessage.tsx +30 -0
  19. package/dist/containers/Storage/StorageGroups/VirtualStorageGroups.tsx +94 -0
  20. package/dist/containers/Storage/StorageGroups/getGroups.ts +21 -0
  21. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +73 -50
  22. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +23 -138
  23. package/dist/containers/Storage/StorageNodes/StorageNodesEmptyDataMessage.tsx +44 -0
  24. package/dist/containers/Storage/StorageNodes/VirtualStorageNodes.tsx +105 -0
  25. package/dist/containers/Storage/StorageNodes/getNodes.ts +26 -0
  26. package/dist/containers/Storage/StorageNodes/getStorageNodesColumns.tsx +125 -0
  27. package/dist/containers/Storage/StorageNodes/shared.ts +9 -0
  28. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +1 -1
  29. package/dist/containers/Storage/StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter.tsx +1 -1
  30. package/dist/containers/Storage/StorageWrapper.tsx +23 -0
  31. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +3 -4
  32. package/dist/containers/Storage/VirtualStorage.tsx +112 -0
  33. package/dist/containers/Storage/i18n/en.json +7 -0
  34. package/dist/containers/Storage/i18n/index.ts +11 -0
  35. package/dist/containers/Storage/i18n/ru.json +7 -0
  36. package/dist/containers/Storage/shared.ts +3 -0
  37. package/dist/containers/Tenants/Tenants.tsx +2 -2
  38. package/dist/containers/UserSettings/i18n/en.json +2 -2
  39. package/dist/containers/UserSettings/i18n/ru.json +2 -2
  40. package/dist/containers/UserSettings/settings.ts +4 -4
  41. package/dist/store/reducers/storage/selectors.ts +0 -20
  42. package/package.json +6 -6
@@ -1,62 +1,30 @@
1
- import cn from 'bem-cn-lite';
1
+ import DataTable, {Settings, SortOrder} from '@gravity-ui/react-data-table';
2
2
 
3
- import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
4
-
5
- import type {ValueOf} from '../../../types/common';
6
3
  import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
7
4
  import type {HandleSort} from '../../../utils/hooks/useTableSort';
8
5
  import type {AdditionalNodesProps} from '../../../types/additionalProps';
9
6
 
10
7
  import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
11
- import {
12
- isSortableNodesProperty,
13
- isUnavailableNode,
14
- NodesUptimeFilterValues,
15
- } from '../../../utils/nodes';
16
-
17
- import {NodeHostWrapper} from '../../../components/NodeHostWrapper/NodeHostWrapper';
8
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
18
9
 
19
- import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
20
- import {PDisk} from '../PDisk';
10
+ import {getPreparedStorageNodesColumns} from './getStorageNodesColumns';
21
11
 
12
+ import {StorageNodesEmptyDataMessage} from './StorageNodesEmptyDataMessage';
13
+ import {getRowUnavailableClassName} from './shared';
22
14
  import i18n from './i18n';
23
15
  import './StorageNodes.scss';
24
16
 
25
- const TableColumnsIds = {
26
- NodeId: 'NodeId',
27
- Host: 'Host',
28
- DC: 'DC',
29
- Rack: 'Rack',
30
- Uptime: 'Uptime',
31
- PDisks: 'PDisks',
32
- Missing: 'Missing',
33
- } as const;
34
-
35
- type TableColumnId = ValueOf<typeof TableColumnsIds>;
36
-
37
17
  interface StorageNodesProps {
38
18
  data: PreparedStorageNode[];
39
19
  tableSettings: Settings;
40
20
  visibleEntities: VisibleEntities;
41
- nodesUptimeFilter: keyof typeof NodesUptimeFilterValues;
21
+ nodesUptimeFilter: NodesUptimeFilterValues;
42
22
  onShowAll?: VoidFunction;
43
23
  additionalNodesProps?: AdditionalNodesProps;
44
24
  sort?: SortOrder;
45
25
  handleSort?: HandleSort;
46
26
  }
47
27
 
48
- const tableColumnsNames: Record<TableColumnId, string> = {
49
- NodeId: 'Node ID',
50
- Host: 'Host',
51
- DC: 'DC',
52
- Rack: 'Rack',
53
- Uptime: 'Uptime',
54
- PDisks: 'PDisks',
55
- Missing: 'Missing',
56
- };
57
-
58
- const b = cn('global-storage-nodes');
59
-
60
28
  export function StorageNodes({
61
29
  data,
62
30
  tableSettings,
@@ -67,106 +35,23 @@ export function StorageNodes({
67
35
  sort,
68
36
  handleSort,
69
37
  }: StorageNodesProps) {
70
- const getNodeRef = additionalNodesProps?.getNodeRef;
71
-
72
- const rawColumns: Column<PreparedStorageNode>[] = [
73
- {
74
- name: TableColumnsIds.NodeId,
75
- header: tableColumnsNames[TableColumnsIds.NodeId],
76
- width: 100,
77
- align: DataTable.RIGHT,
78
- },
79
- {
80
- name: TableColumnsIds.Host,
81
- header: tableColumnsNames[TableColumnsIds.Host],
82
- width: 350,
83
- render: ({row}) => {
84
- return <NodeHostWrapper node={row} getNodeRef={getNodeRef} />;
85
- },
86
- align: DataTable.LEFT,
87
- },
88
- {
89
- name: TableColumnsIds.DC,
90
- header: tableColumnsNames[TableColumnsIds.DC],
91
- render: ({row}) => row.DataCenter || '—',
92
- align: DataTable.LEFT,
93
- },
94
- {
95
- name: TableColumnsIds.Rack,
96
- header: tableColumnsNames[TableColumnsIds.Rack],
97
- render: ({row}) => row.Rack || '—',
98
- align: DataTable.LEFT,
99
- },
100
- {
101
- name: TableColumnsIds.Uptime,
102
- header: tableColumnsNames[TableColumnsIds.Uptime],
103
- width: 130,
104
- sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
105
- align: DataTable.RIGHT,
106
- },
107
- {
108
- name: TableColumnsIds.Missing,
109
- header: tableColumnsNames[TableColumnsIds.Missing],
110
- width: 100,
111
- align: DataTable.CENTER,
112
- defaultOrder: DataTable.DESCENDING,
113
- },
114
- {
115
- name: TableColumnsIds.PDisks,
116
- className: b('pdisks-column'),
117
- header: tableColumnsNames[TableColumnsIds.PDisks],
118
- render: ({row}) => (
119
- <div className={b('pdisks-wrapper')}>
120
- {row.PDisks?.map((pDisk) => (
121
- <div className={b('pdisks-item')} key={pDisk.PDiskId}>
122
- <PDisk data={pDisk} nodeId={row.NodeId} />
123
- </div>
124
- ))}
125
- </div>
126
- ),
127
- align: DataTable.CENTER,
128
- sortable: false,
129
- width: 900,
130
- },
131
- ];
132
-
133
- let columns = rawColumns.map((column) => ({
134
- ...column,
135
- sortable: isSortableNodesProperty(column.name),
136
- }));
137
-
138
- if (visibleEntities !== VISIBLE_ENTITIES.missing) {
139
- columns = columns.filter((col) => col.name !== TableColumnsIds.Missing);
140
- }
141
-
142
- if (!data.length) {
143
- let message;
144
-
145
- if (visibleEntities === VISIBLE_ENTITIES.space) {
146
- message = i18n('empty.out_of_space');
147
- }
148
-
149
- if (visibleEntities === VISIBLE_ENTITIES.missing) {
150
- message = i18n('empty.degraded');
151
- }
152
-
153
- if (nodesUptimeFilter === NodesUptimeFilterValues.SmallUptime) {
154
- message = i18n('empty.small_uptime');
155
- }
156
-
157
- if (
158
- visibleEntities !== VISIBLE_ENTITIES.all &&
159
- nodesUptimeFilter !== NodesUptimeFilterValues.All
160
- ) {
161
- message = i18n('empty.several_filters');
162
- }
163
-
164
- if (message) {
165
- return <EmptyFilter title={message} showAll={i18n('show_all')} onShowAll={onShowAll} />;
166
- }
38
+ const columns = getPreparedStorageNodesColumns(additionalNodesProps, visibleEntities);
39
+
40
+ if (
41
+ !data.length &&
42
+ (visibleEntities !== VISIBLE_ENTITIES.all ||
43
+ nodesUptimeFilter !== NodesUptimeFilterValues.All)
44
+ ) {
45
+ return (
46
+ <StorageNodesEmptyDataMessage
47
+ visibleEntities={visibleEntities}
48
+ nodesUptimeFilter={nodesUptimeFilter}
49
+ onShowAll={onShowAll}
50
+ />
51
+ );
167
52
  }
168
53
 
169
- return data ? (
54
+ return (
170
55
  <DataTable
171
56
  key={visibleEntities as string}
172
57
  theme="yandex-cloud"
@@ -177,9 +62,9 @@ export function StorageNodes({
177
62
  dynamicRenderType: 'variable',
178
63
  }}
179
64
  emptyDataMessage={i18n('empty.default')}
180
- rowClassName={(row) => b('node', {unavailable: isUnavailableNode(row)})}
65
+ rowClassName={getRowUnavailableClassName}
181
66
  sortOrder={sort}
182
67
  onSort={handleSort}
183
68
  />
184
- ) : null;
69
+ );
185
70
  }
@@ -0,0 +1,44 @@
1
+ import type {VisibleEntities} from '../../../store/reducers/storage/types';
2
+ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
3
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
4
+ import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
5
+ import i18n from './i18n';
6
+
7
+ interface StorageNodesEmptyDataMessageProps {
8
+ visibleEntities: VisibleEntities;
9
+ nodesUptimeFilter: NodesUptimeFilterValues;
10
+ onShowAll?: VoidFunction;
11
+ }
12
+
13
+ export const StorageNodesEmptyDataMessage = ({
14
+ visibleEntities,
15
+ nodesUptimeFilter,
16
+ onShowAll,
17
+ }: StorageNodesEmptyDataMessageProps) => {
18
+ let message;
19
+
20
+ if (visibleEntities === VISIBLE_ENTITIES.space) {
21
+ message = i18n('empty.out_of_space');
22
+ }
23
+
24
+ if (visibleEntities === VISIBLE_ENTITIES.missing) {
25
+ message = i18n('empty.degraded');
26
+ }
27
+
28
+ if (nodesUptimeFilter === NodesUptimeFilterValues.SmallUptime) {
29
+ message = i18n('empty.small_uptime');
30
+ }
31
+
32
+ if (
33
+ visibleEntities !== VISIBLE_ENTITIES.all &&
34
+ nodesUptimeFilter !== NodesUptimeFilterValues.All
35
+ ) {
36
+ message = i18n('empty.several_filters');
37
+ }
38
+
39
+ if (message) {
40
+ return <EmptyFilter title={message} showAll={i18n('show_all')} onShowAll={onShowAll} />;
41
+ }
42
+
43
+ return null;
44
+ };
@@ -0,0 +1,105 @@
1
+ import {useCallback, useMemo} from 'react';
2
+
3
+ import type {AdditionalNodesProps} from '../../../types/additionalProps';
4
+ import {
5
+ getUptimeParamValue,
6
+ NodesUptimeFilterValues,
7
+ type NodesSortValue,
8
+ } from '../../../utils/nodes';
9
+ import {
10
+ VirtualTable,
11
+ type FetchData,
12
+ type RenderControls,
13
+ type RenderErrorMessage,
14
+ } from '../../../components/VirtualTable';
15
+ import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
16
+ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
17
+
18
+ import {StorageNodesEmptyDataMessage} from './StorageNodesEmptyDataMessage';
19
+ import {getPreparedStorageNodesColumns} from './getStorageNodesColumns';
20
+ import {getStorageNodes} from './getNodes';
21
+ import {getRowUnavailableClassName} from './shared';
22
+ import i18n from './i18n';
23
+
24
+ interface VirtualStorageNodesProps {
25
+ searchValue: string;
26
+ visibleEntities: VisibleEntities;
27
+ nodesUptimeFilter: NodesUptimeFilterValues;
28
+ tenant?: string;
29
+
30
+ additionalNodesProps?: AdditionalNodesProps;
31
+ onShowAll: VoidFunction;
32
+
33
+ parentContainer?: Element | null;
34
+ renderControls: RenderControls;
35
+ renderErrorMessage: RenderErrorMessage;
36
+ }
37
+
38
+ export const VirtualStorageNodes = ({
39
+ searchValue,
40
+ visibleEntities,
41
+ nodesUptimeFilter,
42
+ tenant,
43
+ additionalNodesProps,
44
+ onShowAll,
45
+ parentContainer,
46
+ renderControls,
47
+ renderErrorMessage,
48
+ }: VirtualStorageNodesProps) => {
49
+ const filters = useMemo(() => {
50
+ return [searchValue, visibleEntities, nodesUptimeFilter, tenant];
51
+ }, [searchValue, visibleEntities, nodesUptimeFilter, tenant]);
52
+
53
+ const fetchData = useCallback<FetchData<PreparedStorageNode>>(
54
+ async (limit, offset, {sortOrder, columnId} = {}) => {
55
+ return await getStorageNodes({
56
+ limit,
57
+ offset,
58
+ filter: searchValue,
59
+ uptime: getUptimeParamValue(nodesUptimeFilter),
60
+ visibleEntities,
61
+ tenant,
62
+
63
+ sortOrder,
64
+ sortValue: columnId as NodesSortValue,
65
+ });
66
+ },
67
+ [nodesUptimeFilter, searchValue, tenant, visibleEntities],
68
+ );
69
+
70
+ const columns = useMemo(() => {
71
+ return getPreparedStorageNodesColumns(additionalNodesProps, visibleEntities);
72
+ }, [additionalNodesProps, visibleEntities]);
73
+
74
+ const renderEmptyDataMessage = () => {
75
+ if (
76
+ visibleEntities !== VISIBLE_ENTITIES.all ||
77
+ nodesUptimeFilter !== NodesUptimeFilterValues.All
78
+ ) {
79
+ return (
80
+ <StorageNodesEmptyDataMessage
81
+ onShowAll={onShowAll}
82
+ nodesUptimeFilter={nodesUptimeFilter}
83
+ visibleEntities={visibleEntities}
84
+ />
85
+ );
86
+ }
87
+
88
+ return i18n('empty.default');
89
+ };
90
+
91
+ return (
92
+ <VirtualTable
93
+ parentContainer={parentContainer}
94
+ columns={columns}
95
+ fetchData={fetchData}
96
+ rowHeight={50}
97
+ limit={50}
98
+ renderControls={renderControls}
99
+ renderErrorMessage={renderErrorMessage}
100
+ renderEmptyDataMessage={renderEmptyDataMessage}
101
+ getRowClassName={getRowUnavailableClassName}
102
+ dependencyArray={filters}
103
+ />
104
+ );
105
+ };
@@ -0,0 +1,26 @@
1
+ import type {NodesApiRequestParams} from '../../../store/reducers/nodes/types';
2
+ import {prepareStorageNodesResponse} from '../../../store/reducers/storage/utils';
3
+
4
+ const getConcurrentId = (limit?: number, offset?: number) => {
5
+ return `getStorageNodes|offset${offset}|limit${limit}`;
6
+ };
7
+
8
+ export const getStorageNodes = async ({
9
+ type = 'static',
10
+ storage = true,
11
+ limit,
12
+ offset,
13
+ ...params
14
+ }: NodesApiRequestParams) => {
15
+ const response = await window.api.getNodes(
16
+ {type, storage, limit, offset, ...params},
17
+ {concurrentId: getConcurrentId(limit, offset)},
18
+ );
19
+ const preparedResponse = prepareStorageNodesResponse(response);
20
+
21
+ return {
22
+ data: preparedResponse.nodes || [],
23
+ found: preparedResponse.found || 0,
24
+ total: preparedResponse.total || 0,
25
+ };
26
+ };
@@ -0,0 +1,125 @@
1
+ import DataTable, {type Column as DataTableColumn} from '@gravity-ui/react-data-table';
2
+
3
+ import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
4
+ import type {AdditionalNodesProps} from '../../../types/additionalProps';
5
+ import type {Column as VirtualTableColumn} from '../../../components/VirtualTable';
6
+ import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
7
+ import {NodeHostWrapper} from '../../../components/NodeHostWrapper/NodeHostWrapper';
8
+ import {isSortableNodesProperty} from '../../../utils/nodes';
9
+
10
+ import {PDisk} from '../PDisk/PDisk';
11
+ import {b} from './shared';
12
+
13
+ export const STORAGE_NODES_COLUMNS_IDS = {
14
+ NodeId: 'NodeId',
15
+ Host: 'Host',
16
+ DC: 'DC',
17
+ Rack: 'Rack',
18
+ Uptime: 'Uptime',
19
+ PDisks: 'PDisks',
20
+ Missing: 'Missing',
21
+ } as const;
22
+
23
+ type StorageGroupsColumn = VirtualTableColumn<PreparedStorageNode> &
24
+ DataTableColumn<PreparedStorageNode>;
25
+
26
+ const getStorageNodesColumns = (additionalNodesProps: AdditionalNodesProps | undefined) => {
27
+ const getNodeRef = additionalNodesProps?.getNodeRef;
28
+
29
+ const columns: StorageGroupsColumn[] = [
30
+ {
31
+ name: STORAGE_NODES_COLUMNS_IDS.NodeId,
32
+ header: 'Node ID',
33
+ width: 100,
34
+ align: DataTable.RIGHT,
35
+ render: ({row}) => row.NodeId,
36
+ },
37
+ {
38
+ name: STORAGE_NODES_COLUMNS_IDS.Host,
39
+ header: 'Host',
40
+ width: 350,
41
+ render: ({row}) => {
42
+ return <NodeHostWrapper node={row} getNodeRef={getNodeRef} />;
43
+ },
44
+ align: DataTable.LEFT,
45
+ },
46
+ {
47
+ name: STORAGE_NODES_COLUMNS_IDS.DC,
48
+ header: 'DC',
49
+ width: 100,
50
+ render: ({row}) => row.DataCenter || '—',
51
+ align: DataTable.LEFT,
52
+ },
53
+ {
54
+ name: STORAGE_NODES_COLUMNS_IDS.Rack,
55
+ header: 'Rack',
56
+ width: 100,
57
+ render: ({row}) => row.Rack || '—',
58
+ align: DataTable.LEFT,
59
+ },
60
+ {
61
+ name: STORAGE_NODES_COLUMNS_IDS.Uptime,
62
+ header: 'Uptime',
63
+ width: 130,
64
+ sortAccessor: ({StartTime}) => (StartTime ? -StartTime : 0),
65
+ align: DataTable.RIGHT,
66
+ render: ({row}) => row.Uptime,
67
+ },
68
+ {
69
+ name: STORAGE_NODES_COLUMNS_IDS.Missing,
70
+ header: 'Missing',
71
+ width: 100,
72
+ align: DataTable.CENTER,
73
+ defaultOrder: DataTable.DESCENDING,
74
+ render: ({row}) => row.Missing,
75
+ },
76
+ {
77
+ name: STORAGE_NODES_COLUMNS_IDS.PDisks,
78
+ className: b('pdisks-column'),
79
+ header: 'PDisks',
80
+ render: ({row}) => {
81
+ return (
82
+ <div className={b('pdisks-wrapper')}>
83
+ {row.PDisks?.map((pDisk) => {
84
+ const vDisks = row.VDisks?.filter(
85
+ (vdisk) => vdisk.PDiskId === pDisk.PDiskId,
86
+ ).map((data) => ({
87
+ ...data,
88
+ NodeId: row.NodeId,
89
+ }));
90
+
91
+ return (
92
+ <div className={b('pdisks-item')} key={pDisk.PDiskId}>
93
+ <PDisk data={pDisk} nodeId={row.NodeId} vDisks={vDisks} />
94
+ </div>
95
+ );
96
+ })}
97
+ </div>
98
+ );
99
+ },
100
+ align: DataTable.CENTER,
101
+ sortable: false,
102
+ width: 900,
103
+ },
104
+ ];
105
+
106
+ return columns;
107
+ };
108
+
109
+ export const getPreparedStorageNodesColumns = (
110
+ additionalNodesProps: AdditionalNodesProps | undefined,
111
+ visibleEntities: VisibleEntities,
112
+ ) => {
113
+ const rawColumns = getStorageNodesColumns(additionalNodesProps);
114
+
115
+ const sortableColumns = rawColumns.map((column) => ({
116
+ ...column,
117
+ sortable: isSortableNodesProperty(column.name),
118
+ }));
119
+
120
+ if (visibleEntities !== VISIBLE_ENTITIES.missing) {
121
+ return sortableColumns.filter((col) => col.name !== STORAGE_NODES_COLUMNS_IDS.Missing);
122
+ }
123
+
124
+ return sortableColumns;
125
+ };
@@ -0,0 +1,9 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import type {PreparedStorageNode} from '../../../store/reducers/storage/types';
4
+ import {isUnavailableNode} from '../../../utils/nodes';
5
+
6
+ export const b = cn('global-storage-nodes');
7
+
8
+ export const getRowUnavailableClassName = (row: PreparedStorageNode) =>
9
+ b('node', {unavailable: isUnavailableNode(row)});
@@ -10,7 +10,7 @@ const StorageTypesTitles = {
10
10
 
11
11
  interface StorageTypeFilterProps {
12
12
  value: StorageType;
13
- onChange: (value: string) => void;
13
+ onChange: (value: StorageType) => void;
14
14
  }
15
15
 
16
16
  const storageTypeFilterQa = 'storage-type-filter';
@@ -11,7 +11,7 @@ export const VisibleEntitiesTitles = {
11
11
 
12
12
  interface StorageProblemFilterProps {
13
13
  value: VisibleEntities;
14
- onChange: (value: string) => void;
14
+ onChange: (value: VisibleEntities) => void;
15
15
  }
16
16
 
17
17
  const storageVisibleEntitiesFilterQa = 'storage-visible-entities-filter';
@@ -0,0 +1,23 @@
1
+ import type {AdditionalNodesProps} from '../../types/additionalProps';
2
+ import {USE_BACKEND_PARAMS_FOR_TABLES_KEY} from '../../utils/constants';
3
+ import {useSetting} from '../../utils/hooks';
4
+
5
+ import {VirtualStorage} from './VirtualStorage';
6
+ import {Storage} from './Storage';
7
+
8
+ interface StorageWrapperProps {
9
+ tenant?: string;
10
+ nodeId?: string;
11
+ parentContainer?: Element | null;
12
+ additionalNodesProps?: AdditionalNodesProps;
13
+ }
14
+
15
+ export const StorageWrapper = ({parentContainer, ...props}: StorageWrapperProps) => {
16
+ const [useVirtualTable] = useSetting<boolean>(USE_BACKEND_PARAMS_FOR_TABLES_KEY);
17
+
18
+ if (useVirtualTable) {
19
+ return <VirtualStorage parentContainer={parentContainer} {...props} />;
20
+ }
21
+
22
+ return <Storage {...props} />;
23
+ };
@@ -10,7 +10,7 @@ import {getUsageSeverityForEntityStatus} from '../utils';
10
10
  import i18n from './i18n';
11
11
  import './UsageFilter.scss';
12
12
 
13
- interface UsageFilterItem {
13
+ export interface UsageFilterItem {
14
14
  threshold: number;
15
15
  count: number;
16
16
  }
@@ -21,13 +21,12 @@ interface UsageFilterProps {
21
21
  groups?: UsageFilterItem[];
22
22
  onChange?: (value: string[]) => void;
23
23
  debounce?: number;
24
- disabled?: boolean;
25
24
  }
26
25
 
27
26
  const b = cn('usage-filter');
28
27
 
29
28
  export const UsageFilter = (props: UsageFilterProps) => {
30
- const {className, value = [], groups = [], onChange, debounce = 200, disabled} = props;
29
+ const {className, value = [], groups = [], onChange, debounce = 200} = props;
31
30
 
32
31
  const [filterValue, setFilterValue] = useState(value);
33
32
  const timer = useRef<number>();
@@ -94,7 +93,7 @@ export const UsageFilter = (props: UsageFilterProps) => {
94
93
  renderOption={renderOption}
95
94
  getOptionHeight={() => 50}
96
95
  popupWidth={280}
97
- disabled={disabled}
96
+ disabled={groups.length === 0}
98
97
  />
99
98
  );
100
99
  };