ydb-embedded-ui 3.4.1 → 3.4.2

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.4.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.1...v3.4.2) (2023-03-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Partitions:** add search to consumers filter ([95e4462](https://github.com/ydb-platform/ydb-embedded-ui/commit/95e446295cb2b2729daf0d0ef719e37c7c8e0d3c))
9
+ * **Partitions:** fix error on wrong consumer in query string ([44269fa](https://github.com/ydb-platform/ydb-embedded-ui/commit/44269fa9240fe31c9ef69e061c20d58b2b55fae3))
10
+ * **PDisk:** display vdisks donors ([8b39b01](https://github.com/ydb-platform/ydb-embedded-ui/commit/8b39b01e8bf62624e9e12ac0a329fda5d03cc8df))
11
+
3
12
  ## [3.4.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.0...v3.4.1) (2023-03-01)
4
13
 
5
14
 
@@ -21,6 +21,21 @@
21
21
  &__vdisks-item {
22
22
  flex-basis: 5px;
23
23
  flex-shrink: 0;
24
+
25
+ .stack__layer {
26
+ background: var(--yc-color-base-background);
27
+
28
+ .data-table__row:hover & {
29
+ background: var(--ydb-data-table-color-hover);
30
+ }
31
+ }
32
+ }
33
+
34
+ &__donors-stack {
35
+ --ydb-stack-offset-x: 0px;
36
+ --ydb-stack-offset-y: -2px;
37
+ --ydb-stack-offset-x-hover: 0px;
38
+ --ydb-stack-offset-y-hover: -7px;
24
39
  }
25
40
 
26
41
  &__media-type {
@@ -2,6 +2,7 @@ import React, {useEffect, useState, useRef, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
 
4
4
  import {InternalLink} from '../../../components/InternalLink';
5
+ import {Stack} from '../../../components/Stack/Stack';
5
6
 
6
7
  import routes, {createHref} from '../../../routes';
7
8
  import {getVDisksForPDisk} from '../../../store/reducers/storage';
@@ -10,6 +11,7 @@ import {TVDiskStateInfo} from '../../../types/api/vdisk';
10
11
  import {stringifyVdiskId} from '../../../utils';
11
12
  import {useTypedSelector} from '../../../utils/hooks';
12
13
  import {getPDiskType} from '../../../utils/pdisk';
14
+ import {isFullVDiksData} from '../../../utils/storage';
13
15
 
14
16
  import {STRUCTURE} from '../../Node/NodePages';
15
17
 
@@ -109,19 +111,42 @@ export const PDisk = ({nodeId, data: rawData = {}}: PDiskProps) => {
109
111
 
110
112
  return (
111
113
  <div className={b('vdisks')}>
112
- {vdisks.map((vdisk) => (
113
- <div
114
- key={stringifyVdiskId(vdisk.VDiskId)}
115
- className={b('vdisks-item')}
116
- style={{
117
- // 1 is small enough for empty disks to be of the minimum width
118
- // but if all of them are empty, `flex-grow: 1` would size them evenly
119
- flexGrow: Number(vdisk.AllocatedSize) || 1,
120
- }}
121
- >
122
- <VDisk data={vdisk} compact />
123
- </div>
124
- ))}
114
+ {vdisks.map((vdisk) => {
115
+ const donors = vdisk.Donors;
116
+
117
+ return (
118
+ <div
119
+ key={stringifyVdiskId(vdisk.VDiskId)}
120
+ className={b('vdisks-item')}
121
+ style={{
122
+ // 1 is small enough for empty disks to be of the minimum width
123
+ // but if all of them are empty, `flex-grow: 1` would size them evenly
124
+ flexGrow: Number(vdisk.AllocatedSize) || 1,
125
+ }}
126
+ >
127
+ {donors && donors.length ? (
128
+ <Stack className={b('donors-stack')} key={stringifyVdiskId(vdisk.VDiskId)}>
129
+ <VDisk data={vdisk} compact />
130
+ {donors.map((donor) => {
131
+ const isFullData = isFullVDiksData(donor);
132
+
133
+ return (
134
+ <VDisk
135
+ compact
136
+ data={isFullData ? donor : {...donor, DonorMode: true}}
137
+ key={stringifyVdiskId(
138
+ isFullData ? donor.VDiskId : donor,
139
+ )}
140
+ />
141
+ );
142
+ })}
143
+ </Stack>
144
+ ) : (
145
+ <VDisk data={vdisk} compact />
146
+ )}
147
+ </div>
148
+ );
149
+ })}
125
150
  </div>
126
151
  );
127
152
  };
@@ -1,8 +1,10 @@
1
1
  .global-storage-nodes {
2
+ &__pdisks-column {
3
+ overflow: visible; // to enable stacked disks overflow the row
4
+ }
5
+
2
6
  &__pdisks-wrapper {
3
7
  display: flex;
4
- overflow-x: auto;
5
- overflow-y: hidden;
6
8
  justify-content: left;
7
9
  align-items: flex-end;
8
10
 
@@ -128,6 +128,7 @@ function StorageNodes({
128
128
  },
129
129
  {
130
130
  name: TableColumnsIds.PDisks,
131
+ className: b('pdisks-column'),
131
132
  header: tableColumnsNames[TableColumnsIds.PDisks],
132
133
  render: ({value, row}) => (
133
134
  <div className={b('pdisks-wrapper')}>
@@ -1,5 +1,5 @@
1
1
  import block from 'bem-cn-lite';
2
- import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
2
+ import {useCallback, useEffect, useMemo, useState} from 'react';
3
3
  import {useDispatch} from 'react-redux';
4
4
  import {escapeRegExp} from 'lodash/fp';
5
5
 
@@ -53,8 +53,6 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
53
53
 
54
54
  const dispatch = useDispatch();
55
55
 
56
- const isFirstRenderRef = useRef(true);
57
-
58
56
  const [generalSearchValue, setGeneralSearchValue] = useState('');
59
57
  const [partitionIdSearchValue, setPartitionIdSearchValue] = useState('');
60
58
 
@@ -74,14 +72,6 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
74
72
  useEffect(() => {
75
73
  // Manual path control to ensure it updates with other values so no request with wrong params will be sent
76
74
  setComponentCurrentPath(path);
77
-
78
- // Do not reset selected consumer on first effect call
79
- // To enable navigating to specific consumer
80
- if (isFirstRenderRef.current) {
81
- isFirstRenderRef.current = false;
82
- } else {
83
- dispatch(setSelectedConsumer(undefined));
84
- }
85
75
  }, [dispatch, path]);
86
76
 
87
77
  const fetchConsumerData = useCallback(
@@ -90,11 +80,11 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
90
80
  dispatch(setDataWasNotLoaded());
91
81
  }
92
82
 
93
- if (selectedConsumer) {
83
+ if (selectedConsumer && consumers && consumers.includes(selectedConsumer)) {
94
84
  dispatch(getConsumer(componentCurrentPath, selectedConsumer));
95
85
  }
96
86
  },
97
- [dispatch, selectedConsumer, componentCurrentPath],
87
+ [dispatch, selectedConsumer, componentCurrentPath, consumers],
98
88
  );
99
89
 
100
90
  useAutofetcher(fetchConsumerData, [fetchConsumerData], autorefresh);
@@ -111,10 +101,13 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
111
101
  );
112
102
 
113
103
  useEffect(() => {
114
- if (consumersToSelect && consumersToSelect.length && !selectedConsumer) {
104
+ const shouldUpdateSelectedConsumer =
105
+ !selectedConsumer || (consumers && !consumers.includes(selectedConsumer));
106
+
107
+ if (consumersToSelect && consumersToSelect.length && shouldUpdateSelectedConsumer) {
115
108
  dispatch(setSelectedConsumer(consumersToSelect[0].value));
116
109
  }
117
- }, [dispatch, consumersToSelect, selectedConsumer]);
110
+ }, [dispatch, consumersToSelect, selectedConsumer, consumers]);
118
111
 
119
112
  const selectedColumns: string[] = useMemo(
120
113
  () =>
@@ -222,6 +215,7 @@ export const Partitions = ({path, type, nodes, consumers}: PartitionsProps) => {
222
215
  options={consumersToSelect}
223
216
  value={[selectedConsumer || '']}
224
217
  onUpdate={handleConsumerSelectChange}
218
+ filterable={consumers && consumers.length > 5}
225
219
  />
226
220
  <Search
227
221
  onChange={handlePartitionIdSearchChange}
@@ -6,6 +6,7 @@ import type {EPathType} from '../../../../types/api/schema';
6
6
  import {useTypedSelector} from '../../../../utils/hooks';
7
7
 
8
8
  import {
9
+ cleanTopicData,
9
10
  getTopic,
10
11
  setDataWasNotLoaded as setTopicDataWasNotLoaded,
11
12
  } from '../../../../store/reducers/topic';
@@ -61,6 +62,7 @@ export const PartitionsWrapper = ({path, type}: PartitionsWrapperProps) => {
61
62
 
62
63
  useEffect(() => {
63
64
  dispatch(setTopicDataWasNotLoaded());
65
+ dispatch(cleanTopicData());
64
66
  dispatch(setNodesDataWasNotLoaded());
65
67
 
66
68
  dispatch(getTopic(path));
@@ -18,6 +18,7 @@ import {convertBytesObjectToSpeed} from '../../utils/bytesParsers';
18
18
  export const FETCH_TOPIC = createRequestActionTypes('topic', 'FETCH_TOPIC');
19
19
 
20
20
  const SET_DATA_WAS_NOT_LOADED = 'topic/SET_DATA_WAS_NOT_LOADED';
21
+ const CLEAN_TOPIC_DATA = 'topic/CLEAN_TOPIC_DATA';
21
22
 
22
23
  const initialState = {
23
24
  loading: true,
@@ -64,6 +65,12 @@ const topic: Reducer<ITopicState, ITopicAction> = (state = initialState, action)
64
65
  wasLoaded: false,
65
66
  };
66
67
  }
68
+ case CLEAN_TOPIC_DATA: {
69
+ return {
70
+ ...state,
71
+ data: undefined,
72
+ };
73
+ }
67
74
  default:
68
75
  return state;
69
76
  }
@@ -75,6 +82,12 @@ export const setDataWasNotLoaded = () => {
75
82
  } as const;
76
83
  };
77
84
 
85
+ export const cleanTopicData = () => {
86
+ return {
87
+ type: CLEAN_TOPIC_DATA,
88
+ } as const;
89
+ };
90
+
78
91
  export function getTopic(path?: string) {
79
92
  return createApiRequest({
80
93
  request: window.api.getTopic({path}),
@@ -1,4 +1,4 @@
1
- import {FETCH_TOPIC, setDataWasNotLoaded} from '../../store/reducers/topic';
1
+ import {FETCH_TOPIC, cleanTopicData, setDataWasNotLoaded} from '../../store/reducers/topic';
2
2
  import type {ApiRequestAction} from '../../store/utils';
3
3
  import type {IProcessSpeedStats} from '../../utils/bytesParsers';
4
4
  import type {IResponseError} from '../api/error';
@@ -31,7 +31,8 @@ export interface ITopicState {
31
31
 
32
32
  export type ITopicAction =
33
33
  | ApiRequestAction<typeof FETCH_TOPIC, DescribeTopicResult, IResponseError>
34
- | ReturnType<typeof setDataWasNotLoaded>;
34
+ | ReturnType<typeof setDataWasNotLoaded>
35
+ | ReturnType<typeof cleanTopicData>;
35
36
 
36
37
  export interface ITopicRootStateSlice {
37
38
  topic: ITopicState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "3.4.1",
3
+ "version": "3.4.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],