Skip to content

Commit 1f8d6fc

Browse files
authored
feat: vdisks in 2 rows (#1758)
1 parent 6be903d commit 1f8d6fc

File tree

10 files changed

+271
-24
lines changed

10 files changed

+271
-24
lines changed

src/containers/Storage/Disks/Disks.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727

2828
&__pdisk-item {
29-
width: 80px;
29+
min-width: 80px;
3030
}
3131
&__pdisk-progress-bar {
3232
--progress-bar-full-height: 20px;

src/containers/Storage/PDisk/PDisk.scss

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,42 @@
11
.pdisk-storage {
2+
--pdisk-vdisk-width: 3px;
3+
--pdisk-gap-width: 2px;
4+
25
position: relative;
36

4-
width: 120px;
7+
display: flex;
8+
flex-direction: column;
9+
justify-content: flex-end;
10+
11+
width: calc(
12+
var(--pdisk-max-slots, 1) * var(--pdisk-vdisk-width) + (var(--pdisk-max-slots, 1) - 1) *
13+
var(--pdisk-gap-width)
14+
);
15+
min-width: 120px;
516

617
&__content {
718
position: relative;
819

920
display: block;
21+
flex: 1;
1022

11-
border-radius: 4px; // to match interactive area with disk shape
23+
border-radius: 4px;
1224
}
1325

1426
&__vdisks {
1527
display: flex;
16-
// this breaks disks relative sizes, but disks rarely exceed one line
17-
flex-wrap: wrap;
18-
gap: 2px;
28+
flex: 0 0 auto;
29+
gap: var(--pdisk-gap-width);
1930

2031
margin-bottom: 4px;
32+
33+
white-space: nowrap;
2134
}
2235

2336
&__vdisks-item {
24-
flex-basis: 3px;
25-
flex-shrink: 0;
37+
flex: 0 0 var(--pdisk-vdisk-width);
38+
39+
min-width: var(--pdisk-vdisk-width);
2640

2741
.stack__layer {
2842
.data-table__row:hover & {

src/containers/Storage/PDisk/PDisk.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import './PDisk.scss';
1616

1717
const b = cn('pdisk-storage');
1818

19+
const PDISK_MAX_SLOTS_CSS_VAR = '--pdisk-max-slots';
20+
1921
interface PDiskProps {
2022
data?: PreparedPDisk;
2123
vDisks?: PreparedVDisk[];
@@ -25,6 +27,7 @@ interface PDiskProps {
2527
className?: string;
2628
progressBarClassName?: string;
2729
viewContext?: StorageViewContext;
30+
maximumSlotsPerDisk?: string;
2831
}
2932

3033
export const PDisk = ({
@@ -36,6 +39,7 @@ export const PDisk = ({
3639
className,
3740
progressBarClassName,
3841
viewContext,
42+
maximumSlotsPerDisk,
3943
}: PDiskProps) => {
4044
const {NodeId, PDiskId} = data;
4145
const pDiskIdsDefined = valueIsDefined(NodeId) && valueIsDefined(PDiskId);
@@ -73,7 +77,17 @@ export const PDisk = ({
7377
}
7478

7579
return (
76-
<div className={b(null, className)} ref={anchorRef}>
80+
<div
81+
className={b(null, className)}
82+
ref={anchorRef}
83+
style={
84+
maximumSlotsPerDisk
85+
? ({
86+
[PDISK_MAX_SLOTS_CSS_VAR]: maximumSlotsPerDisk,
87+
} as React.CSSProperties)
88+
: undefined
89+
}
90+
>
7791
{renderVDisks()}
7892
<HoverPopup
7993
showPopup={showPopup}

src/containers/Storage/StorageNodes/columns/StorageNodesColumns.scss

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,14 @@
55

66
&__pdisks-wrapper {
77
display: flex;
8-
justify-content: left;
9-
align-items: flex-end;
8+
gap: 10px;
109

11-
width: max-content;
10+
width: 100%;
1211
height: 40px;
1312
}
14-
&__pdisks-item {
15-
flex-grow: 1;
16-
17-
max-width: 200px;
18-
margin-right: 10px;
1913

20-
&:last-child {
21-
margin-right: 0px;
22-
}
14+
&__pdisks-item {
15+
display: flex;
16+
flex-shrink: 0;
2317
}
2418
}

src/containers/Storage/StorageNodes/columns/columns.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ const getPDisksColumn = ({viewContext}: GetStorageNodesColumnsParams): StorageNo
4545

4646
return (
4747
<div className={b('pdisks-item')} key={pDisk.PDiskId}>
48-
<PDisk data={pDisk} vDisks={vDisks} viewContext={viewContext} />
48+
<PDisk
49+
data={pDisk}
50+
vDisks={vDisks}
51+
viewContext={viewContext}
52+
maximumSlotsPerDisk={row.MaximumSlotsPerDisk}
53+
/>
4954
</div>
5055
);
5156
})}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import type {TNodeInfo} from '../../../../types/api/nodes';
2+
import {TPDiskState} from '../../../../types/api/pdisk';
3+
import {EVDiskState} from '../../../../types/api/vdisk';
4+
import type {TVDiskID} from '../../../../types/api/vdisk';
5+
import {calculateMaximumSlotsPerDisk} from '../utils';
6+
7+
const createVDiskId = (id: number): TVDiskID => ({
8+
GroupID: id,
9+
GroupGeneration: 1,
10+
Ring: 1,
11+
Domain: 1,
12+
VDisk: id,
13+
});
14+
15+
describe('calculateMaximumSlotsPerDisk', () => {
16+
it('should return providedMaximumSlotsPerDisk when it is provided', () => {
17+
const nodes: TNodeInfo[] = [];
18+
const providedMaximumSlotsPerDisk = '5';
19+
20+
expect(calculateMaximumSlotsPerDisk(nodes, providedMaximumSlotsPerDisk)).toBe('5');
21+
});
22+
23+
it('should return "1" for empty nodes array', () => {
24+
const nodes: TNodeInfo[] = [];
25+
26+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
27+
});
28+
29+
it('should return "1" for undefined nodes', () => {
30+
expect(calculateMaximumSlotsPerDisk(undefined)).toBe('1');
31+
});
32+
33+
it('should return "1" for nodes without PDisks or VDisks', () => {
34+
const nodes: TNodeInfo[] = [
35+
{
36+
NodeId: 1,
37+
SystemState: {},
38+
},
39+
];
40+
41+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
42+
});
43+
44+
it('should calculate maximum slots correctly for single node with one PDisk and multiple VDisks', () => {
45+
const nodes: TNodeInfo[] = [
46+
{
47+
NodeId: 1,
48+
SystemState: {},
49+
PDisks: [
50+
{
51+
PDiskId: 1,
52+
State: TPDiskState.Normal,
53+
},
54+
],
55+
VDisks: [
56+
{
57+
VDiskId: createVDiskId(1),
58+
PDiskId: 1,
59+
VDiskState: EVDiskState.OK,
60+
},
61+
{
62+
VDiskId: createVDiskId(2),
63+
PDiskId: 1,
64+
VDiskState: EVDiskState.OK,
65+
},
66+
],
67+
},
68+
];
69+
70+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('2');
71+
});
72+
73+
it('should calculate maximum slots across multiple nodes', () => {
74+
const nodes: TNodeInfo[] = [
75+
{
76+
NodeId: 1,
77+
SystemState: {},
78+
PDisks: [
79+
{
80+
PDiskId: 1,
81+
State: TPDiskState.Normal,
82+
},
83+
],
84+
VDisks: [
85+
{
86+
VDiskId: createVDiskId(1),
87+
PDiskId: 1,
88+
VDiskState: EVDiskState.OK,
89+
},
90+
],
91+
},
92+
{
93+
NodeId: 2,
94+
SystemState: {},
95+
PDisks: [
96+
{
97+
PDiskId: 2,
98+
State: TPDiskState.Normal,
99+
},
100+
],
101+
VDisks: [
102+
{
103+
VDiskId: createVDiskId(2),
104+
PDiskId: 2,
105+
VDiskState: EVDiskState.OK,
106+
},
107+
{
108+
VDiskId: createVDiskId(3),
109+
PDiskId: 2,
110+
VDiskState: EVDiskState.OK,
111+
},
112+
{
113+
VDiskId: createVDiskId(4),
114+
PDiskId: 2,
115+
VDiskState: EVDiskState.OK,
116+
},
117+
],
118+
},
119+
];
120+
121+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('3');
122+
});
123+
124+
it('should handle nodes with multiple PDisks', () => {
125+
const nodes: TNodeInfo[] = [
126+
{
127+
NodeId: 1,
128+
SystemState: {},
129+
PDisks: [
130+
{
131+
PDiskId: 1,
132+
State: TPDiskState.Normal,
133+
},
134+
{
135+
PDiskId: 2,
136+
State: TPDiskState.Normal,
137+
},
138+
],
139+
VDisks: [
140+
{
141+
VDiskId: createVDiskId(1),
142+
PDiskId: 1,
143+
VDiskState: EVDiskState.OK,
144+
},
145+
{
146+
VDiskId: createVDiskId(2),
147+
PDiskId: 1,
148+
VDiskState: EVDiskState.OK,
149+
},
150+
{
151+
VDiskId: createVDiskId(3),
152+
PDiskId: 2,
153+
VDiskState: EVDiskState.OK,
154+
},
155+
],
156+
},
157+
];
158+
159+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('2');
160+
});
161+
162+
it('should ignore VDisks with non-matching PDiskId', () => {
163+
const nodes: TNodeInfo[] = [
164+
{
165+
NodeId: 1,
166+
SystemState: {},
167+
PDisks: [
168+
{
169+
PDiskId: 1,
170+
State: TPDiskState.Normal,
171+
},
172+
],
173+
VDisks: [
174+
{
175+
VDiskId: createVDiskId(1),
176+
PDiskId: 1,
177+
VDiskState: EVDiskState.OK,
178+
},
179+
{
180+
VDiskId: createVDiskId(2),
181+
PDiskId: 2, // Non-matching PDiskId
182+
VDiskState: EVDiskState.OK,
183+
},
184+
],
185+
},
186+
];
187+
188+
expect(calculateMaximumSlotsPerDisk(nodes)).toBe('1');
189+
});
190+
});

src/store/reducers/storage/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface PreparedStorageNode extends PreparedNodeSystemState {
3535
VDisks?: PreparedVDisk[];
3636

3737
Missing: number;
38+
MaximumSlotsPerDisk: string;
3839
}
3940

4041
export interface PreparedStorageGroupFilters {

0 commit comments

Comments
 (0)