Skip to content

Commit 558de75

Browse files
authored
feat: implement PDisk & VDisk info tooltips (#4397)
1 parent b0cdb31 commit 558de75

84 files changed

Lines changed: 3020 additions & 894 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎src/components/DiskCapacityInfo/DiskCapacityInfo.tsx‎

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ import type {PreparedStorageGroup} from '../../store/reducers/storage/types';
66
import {isCapacityAlert} from '../../types/api/enums';
77
import {getCapacityAlertTheme, normalizeCapacityAlert} from '../../utils/capacityAlerts';
88
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
9+
import type {DiskDetailItem} from '../../utils/disks/diskInfo/getDiskLocationItems';
910
import type {PreparedPDisk, PreparedVDisk} from '../../utils/disks/types';
1011
import {
1112
formatMetricCountPair,
1213
formatMetricPercent,
1314
formatNormalizedMetricPercent,
1415
formatStorageMetricPair,
1516
} from '../../utils/storageMetrics';
17+
import {DiskCapacityAlertLabel} from '../DiskStatus/DiskStatus';
1618
import type {InfoViewerItem} from '../InfoViewer';
19+
import {pDiskInfoKeyset} from '../PDiskInfo/i18n';
1720
import {TitleWithHelpMark} from '../TitleWithHelpmark/TitleWithHelpmark';
21+
import {vDiskInfoKeyset} from '../VDiskInfo/i18n';
1822
import type {YDBDefinitionListItem} from '../YDBDefinitionList/YDBDefinitionList';
1923
import {
2024
CAPACITY_CONFIGURATION_HELP_TEXT,
@@ -98,22 +102,36 @@ export function getVDiskCapacityInfoItems(
98102

99103
export function getPDiskCapacityInfoItems(
100104
data: PreparedPDisk | undefined,
101-
{withUsage, withCapacityAlert}: {withUsage: boolean; withCapacityAlert: boolean},
105+
{
106+
withUsage,
107+
withCapacityAlert,
108+
fixedDecimalPlaces,
109+
useWhiteboardSize = true,
110+
}: {
111+
withUsage: boolean;
112+
withCapacityAlert: boolean;
113+
fixedDecimalPlaces?: number;
114+
useWhiteboardSize?: boolean;
115+
},
102116
): DiskCapacityInfoItem[] {
103-
const sizeData = data?.WhiteboardSize ?? data;
117+
const sizeData = useWhiteboardSize ? (data?.WhiteboardSize ?? data) : data;
104118
const items: DiskCapacityInfoItem[] = [
105119
{
106120
id: 'space',
107121
title: i18n('field_space'),
108-
value: formatStorageMetricPair(sizeData?.AllocatedSize, sizeData?.TotalSize),
122+
value: formatStorageMetricPair(
123+
sizeData?.AllocatedSize,
124+
sizeData?.TotalSize,
125+
fixedDecimalPlaces,
126+
),
109127
},
110128
];
111129

112130
if (withUsage) {
113131
items.push({
114132
id: 'pdisk-usage',
115133
title: CAPACITY_METRICS_COLUMN_TITLES.MaxPDiskUsage,
116-
value: formatMetricPercent(data?.PDiskUsage),
134+
value: formatMetricPercent(data?.PDiskUsage, fixedDecimalPlaces),
117135
note: CAPACITY_METRICS_HELP_TEXT.MaxPDiskUsage,
118136
});
119137
}
@@ -144,6 +162,80 @@ export function getPDiskCapacityInfoItems(
144162
return items;
145163
}
146164

165+
export function getVDiskCapacityItems(
166+
data: PreparedVDisk,
167+
{capacityMetricsEnabled}: {capacityMetricsEnabled: boolean},
168+
): DiskDetailItem[] {
169+
const size: Pick<PreparedVDisk, 'AllocatedSize' | 'SizeLimit' | 'HasCompleteSizeData'> =
170+
capacityMetricsEnabled ? (data.WhiteboardSize ?? data) : data;
171+
const items: DiskDetailItem[] = [
172+
{
173+
id: 'group-size-in-units',
174+
name: vDiskInfoKeyset('field_group-size-in-units'),
175+
content: formatCapacityUnitCount(data.GroupSizeInUnits),
176+
note: CAPACITY_CONFIGURATION_HELP_TEXT.GroupSizeInUnits,
177+
},
178+
{
179+
id: 'size',
180+
name: vDiskInfoKeyset('size'),
181+
content:
182+
size.HasCompleteSizeData === false
183+
? EMPTY_DATA_PLACEHOLDER
184+
: formatStorageMetricPair(size.AllocatedSize, size.SizeLimit, 2),
185+
},
186+
{
187+
id: 'capacity-alert',
188+
name: vDiskInfoKeyset('field_capacity-alert'),
189+
content: <DiskCapacityAlertLabel value={data.CapacityAlert} />,
190+
note: CAPACITY_METRICS_HELP_TEXT.CapacityAlert,
191+
},
192+
{
193+
id: 'vdisk-slot-usage',
194+
name: CAPACITY_METRICS_COLUMN_TITLES.MaxVDiskSlotUsage,
195+
content: formatMetricPercent(data.VDiskSlotUsage, 2),
196+
note: CAPACITY_METRICS_HELP_TEXT.MaxVDiskSlotUsage,
197+
},
198+
{
199+
id: 'vdisk-raw-usage',
200+
name: CAPACITY_METRICS_COLUMN_TITLES.MaxVDiskRawUsage,
201+
content: formatMetricPercent(data.VDiskRawUsage, 2),
202+
note: CAPACITY_METRICS_HELP_TEXT.MaxVDiskRawUsage,
203+
},
204+
];
205+
return items.filter(({id}) => capacityMetricsEnabled || id === 'size');
206+
}
207+
208+
export function getPDiskCapacityItems(
209+
data: PreparedPDisk,
210+
{useWhiteboardSize = true}: {useWhiteboardSize?: boolean} = {},
211+
): DiskDetailItem[] {
212+
const fieldOrder = ['slot-size-in-units', 'space', 'capacity-alert', 'pdisk-usage', 'slots'];
213+
const capacityItems = getPDiskCapacityInfoItems(data, {
214+
withUsage: true,
215+
withCapacityAlert: true,
216+
fixedDecimalPlaces: 2,
217+
useWhiteboardSize,
218+
});
219+
const items: DiskDetailItem[] = [];
220+
for (const id of fieldOrder) {
221+
const field = capacityItems.find((item) => item.id === id);
222+
if (field) {
223+
items.push({
224+
id,
225+
name: field.title,
226+
content:
227+
id === 'capacity-alert' ? (
228+
<DiskCapacityAlertLabel value={data.PDiskCapacityAlert} />
229+
) : (
230+
field.value
231+
),
232+
note: id === 'slots' ? pDiskInfoKeyset('context_slots') : field.note,
233+
});
234+
}
235+
}
236+
return items;
237+
}
238+
147239
export function getStorageGroupCapacityInfoItems(
148240
data: PreparedStorageGroup | undefined,
149241
): DiskCapacityInfoItem[] {

‎src/components/DiskCapacityInfo/__test__/DiskCapacityInfo.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,24 @@ describe('DiskCapacityInfo builders', () => {
344344
expect(spaceItem.value).toBe(EMPTY_DATA_PLACEHOLDER);
345345
});
346346

347+
test('uses legacy PDisk size when Whiteboard size is disabled', () => {
348+
const [spaceItem] = getPDiskCapacityInfoItems(
349+
{
350+
AllocatedSize: 40_000_000_000,
351+
TotalSize: 100_000_000_000,
352+
WhiteboardSize: {TotalSize: 22_000_000_000},
353+
},
354+
{
355+
withUsage: false,
356+
withCapacityAlert: false,
357+
fixedDecimalPlaces: 2,
358+
useWhiteboardSize: false,
359+
},
360+
);
361+
362+
expect(spaceItem.value).toBe(`40.00 / 100.00${UNBREAKABLE_GAP}GB`);
363+
});
364+
347365
test('formats normalized storage-group scalar values without rendering a component', () => {
348366
const items = getStorageGroupCapacityInfoItems({
349367
Degraded: 0,
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
.ydb-disk-popup {
2+
--ydb-disk-popup-base-width: 368px;
3+
--ydb-disk-popup-width-growth: 50px;
4+
5+
width: max-content;
6+
min-width: min(var(--ydb-disk-popup-base-width), calc(100vw - 64px));
7+
max-width: min(
8+
calc(var(--ydb-disk-popup-base-width) + var(--ydb-disk-popup-width-growth)),
9+
calc(100vw - 64px)
10+
);
11+
12+
font-size: var(--g-text-body-1-font-size);
13+
line-height: var(--g-text-body-1-line-height);
14+
overflow-wrap: anywhere;
15+
16+
&_combined {
17+
--ydb-disk-popup-base-width: 593px;
18+
--ydb-disk-popup-width-growth: 120px;
19+
--ydb-pdisk-log-size-progress-width: 50px;
20+
21+
display: grid;
22+
grid-template-columns: repeat(2, minmax(0, 1fr));
23+
}
24+
25+
&__panel {
26+
display: flex;
27+
flex: 1;
28+
flex-direction: column;
29+
gap: var(--g-spacing-2);
30+
31+
min-width: 0;
32+
}
33+
34+
&_combined &__panel:first-child {
35+
padding-right: var(--g-spacing-3);
36+
37+
border-right: 1px solid var(--g-color-line-generic);
38+
}
39+
40+
&_combined &__panel + &__panel {
41+
padding-left: var(--g-spacing-3);
42+
}
43+
44+
&__body,
45+
&__footer {
46+
display: flex;
47+
flex-direction: column;
48+
gap: var(--g-spacing-2);
49+
}
50+
51+
// Only Compaction contributes to the intrinsic width; other sections fit the card.
52+
&__body > :not(:has(.ydb-vdisk-popup__compaction)),
53+
&__footer,
54+
.g-definition-list__item:not(:has(.ydb-vdisk-popup__compaction)) {
55+
contain: inline-size;
56+
}
57+
58+
.ydb-vdisk-popup__compaction {
59+
width: max-content;
60+
max-width: 100%;
61+
}
62+
63+
.g-definition-list__item:has(.ydb-vdisk-popup__compaction) {
64+
.g-definition-list__term-container {
65+
flex-shrink: 1;
66+
}
67+
68+
.g-definition-list__definition {
69+
flex-shrink: 0;
70+
}
71+
}
72+
73+
&__footer {
74+
margin-top: auto;
75+
}
76+
77+
&__id {
78+
min-width: 0;
79+
overflow-wrap: anywhere;
80+
}
81+
82+
.g-definition-list {
83+
--g-definition-list-item-gap: var(--g-spacing-1);
84+
}
85+
86+
.g-definition-list__definition {
87+
min-width: 0;
88+
overflow-wrap: anywhere;
89+
}
90+
91+
.g-definition-list__copy-container {
92+
min-width: 0;
93+
max-width: 100%;
94+
}
95+
96+
.g-definition-list__dots {
97+
flex-basis: 0;
98+
99+
min-width: 0;
100+
}
101+
102+
.ydb-disk-status__tooltip {
103+
min-width: 0;
104+
max-width: 100%;
105+
}
106+
107+
.ydb-disk-status__label {
108+
min-width: 0;
109+
max-width: 100%;
110+
height: auto;
111+
min-height: var(--_--height);
112+
113+
.g-label__text {
114+
display: block;
115+
overflow: visible;
116+
117+
min-width: 0;
118+
119+
text-align: start;
120+
white-space: nowrap;
121+
}
122+
123+
.g-label__content,
124+
.g-label__value,
125+
.g-label__key,
126+
.g-label__separator {
127+
display: inline;
128+
overflow: visible;
129+
130+
text-overflow: clip;
131+
}
132+
}
133+
134+
.ydb-pdisk-log-size,
135+
.ydb-vdisk-status__replication-progress {
136+
flex-wrap: wrap;
137+
138+
max-width: 100%;
139+
140+
white-space: normal;
141+
}
142+
143+
&__location {
144+
contain: inline-size;
145+
146+
padding: var(--g-spacing-1) var(--g-spacing-2);
147+
148+
border-radius: var(--g-border-radius-m);
149+
background-color: var(--g-color-base-generic);
150+
151+
.g-definition-list__dots {
152+
display: none;
153+
}
154+
155+
.g-definition-list__definition {
156+
flex: 1;
157+
}
158+
}
159+
160+
&__text {
161+
overflow: hidden;
162+
163+
min-width: 0;
164+
165+
cursor: default;
166+
white-space: nowrap;
167+
text-overflow: ellipsis;
168+
169+
&_with-left-trim {
170+
text-align: end;
171+
direction: rtl;
172+
173+
> span {
174+
unicode-bidi: plaintext;
175+
}
176+
}
177+
}
178+
179+
&__text-tooltip {
180+
overflow-wrap: anywhere;
181+
}
182+
183+
@media (max-width: 700px) {
184+
&_combined {
185+
--ydb-disk-popup-base-width: 368px;
186+
187+
grid-template-columns: minmax(0, 1fr);
188+
}
189+
190+
&_combined &__panel:first-child {
191+
padding-right: 0;
192+
padding-bottom: var(--g-spacing-3);
193+
194+
border-right: none;
195+
border-bottom: 1px solid var(--g-color-line-generic);
196+
}
197+
198+
&_combined &__panel + &__panel {
199+
padding-top: var(--g-spacing-3);
200+
padding-left: 0;
201+
}
202+
}
203+
}

0 commit comments

Comments
 (0)