ydb-embedded-ui 22.4.0 → 22.5.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 (266) hide show
  1. package/dist/components/ButtonWithConfirmDialog/ButtonWithConfirmDialog.d.ts +3 -2
  2. package/dist/components/ButtonWithConfirmDialog/ButtonWithConfirmDialog.js +19 -4
  3. package/dist/components/ButtonWithConfirmDialog/ButtonWithConfirmDialog.js.map +1 -1
  4. package/dist/components/CopyableValue/CopyableValue.d.ts +10 -0
  5. package/dist/components/CopyableValue/CopyableValue.js +10 -0
  6. package/dist/components/CopyableValue/CopyableValue.js.map +1 -0
  7. package/dist/components/CopyableValue/CopyableValue.scss +16 -0
  8. package/dist/components/CopyableValue/i18n/en.json +3 -0
  9. package/dist/components/CopyableValue/i18n/index.d.ts +2 -0
  10. package/dist/components/CopyableValue/i18n/index.js +6 -0
  11. package/dist/components/CopyableValue/i18n/index.js.map +1 -0
  12. package/dist/components/CriticalActionDialog/CriticalActionDialog.d.ts +2 -1
  13. package/dist/components/CriticalActionDialog/CriticalActionDialog.js +2 -1
  14. package/dist/components/CriticalActionDialog/CriticalActionDialog.js.map +1 -1
  15. package/dist/components/DiskCapacityInfo/DiskCapacityInfo.d.ts +1 -21
  16. package/dist/components/DiskCapacityInfo/DiskCapacityInfo.js +40 -126
  17. package/dist/components/DiskCapacityInfo/DiskCapacityInfo.js.map +1 -1
  18. package/dist/components/DiskCapacityInfo/i18n/en.json +0 -1
  19. package/dist/components/DiskCapacityInfo/i18n/index.d.ts +1 -1
  20. package/dist/components/EntityStatus/EntityStatus.d.ts +18 -6
  21. package/dist/components/EntityStatus/EntityStatus.js +5 -1
  22. package/dist/components/EntityStatus/EntityStatus.js.map +1 -1
  23. package/dist/components/EntityStatus/EntityStatus.scss +5 -0
  24. package/dist/components/EvictVDiskButton/EvictVDiskButton.d.ts +5 -2
  25. package/dist/components/EvictVDiskButton/EvictVDiskButton.js +3 -2
  26. package/dist/components/EvictVDiskButton/EvictVDiskButton.js.map +1 -1
  27. package/dist/components/EvictVDiskButton/i18n/en.json +2 -2
  28. package/dist/components/FullNodeViewer/FullNodeViewer.js +4 -7
  29. package/dist/components/FullNodeViewer/FullNodeViewer.js.map +1 -1
  30. package/dist/components/FullNodeViewer/i18n/index.d.ts +1 -1
  31. package/dist/components/HoverPopup/HoverPopup.d.ts +1 -0
  32. package/dist/components/HoverPopup/HoverPopup.js +87 -16
  33. package/dist/components/HoverPopup/HoverPopup.js.map +1 -1
  34. package/dist/components/MemoryViewer/MemoryViewer.d.ts +3 -1
  35. package/dist/components/MemoryViewer/MemoryViewer.js +25 -16
  36. package/dist/components/MemoryViewer/MemoryViewer.js.map +1 -1
  37. package/dist/components/MemoryViewer/i18n/en.json +4 -3
  38. package/dist/components/MemoryViewer/i18n/index.d.ts +1 -1
  39. package/dist/components/MemoryViewer/utils.d.ts +4 -2
  40. package/dist/components/MemoryViewer/utils.js +11 -11
  41. package/dist/components/MemoryViewer/utils.js.map +1 -1
  42. package/dist/components/PDiskInfo/PDiskInfo.d.ts +4 -9
  43. package/dist/components/PDiskInfo/PDiskInfo.js +32 -144
  44. package/dist/components/PDiskInfo/PDiskInfo.js.map +1 -1
  45. package/dist/components/PDiskInfo/PDiskInfo.scss +15 -0
  46. package/dist/components/PDiskInfo/PDiskLogSize.js +1 -1
  47. package/dist/components/PDiskInfo/PDiskLogSize.js.map +1 -1
  48. package/dist/components/PDiskInfo/i18n/en.json +6 -3
  49. package/dist/components/PDiskInfo/i18n/index.d.ts +1 -1
  50. package/dist/components/PDiskInfo/statuses.js +12 -3
  51. package/dist/components/PDiskInfo/statuses.js.map +1 -1
  52. package/dist/components/Search/Search.js +3 -1
  53. package/dist/components/Search/Search.js.map +1 -1
  54. package/dist/components/StorageGroupInfo/StorageGroupInfo.js +4 -9
  55. package/dist/components/StorageGroupInfo/StorageGroupInfo.js.map +1 -1
  56. package/dist/components/StorageGroupInfo/i18n/index.d.ts +1 -1
  57. package/dist/components/TenantNameWrapper/i18n/index.d.ts +1 -1
  58. package/dist/components/VDisk/VDisk.js +2 -2
  59. package/dist/components/VDisk/VDisk.js.map +1 -1
  60. package/dist/components/VDiskInfo/VDiskInfo.d.ts +3 -7
  61. package/dist/components/VDiskInfo/VDiskInfo.js +57 -235
  62. package/dist/components/VDiskInfo/VDiskInfo.js.map +1 -1
  63. package/dist/components/VDiskInfo/VDiskInfo.scss +19 -3
  64. package/dist/components/VDiskInfo/getVDiskDetails.d.ts +7 -1
  65. package/dist/components/VDiskInfo/getVDiskDetails.js +14 -1
  66. package/dist/components/VDiskInfo/getVDiskDetails.js.map +1 -1
  67. package/dist/components/VDiskInfo/i18n/en.json +4 -13
  68. package/dist/components/VDiskInfo/i18n/index.d.ts +1 -1
  69. package/dist/components/VDiskPopup/VDiskPopup.d.ts +2 -1
  70. package/dist/components/VDiskPopup/VDiskPopup.js +10 -26
  71. package/dist/components/VDiskPopup/VDiskPopup.js.map +1 -1
  72. package/dist/components/VDiskPopup/i18n/en.json +0 -2
  73. package/dist/components/VDiskPopup/i18n/index.d.ts +1 -1
  74. package/dist/components/VDiskStatus/VDiskReplicationStatus.js +1 -1
  75. package/dist/components/VDiskStatus/VDiskReplicationStatus.js.map +1 -1
  76. package/dist/components/VDiskStatus/VDiskStatus.js +2 -2
  77. package/dist/components/VDiskStatus/VDiskStatus.js.map +1 -1
  78. package/dist/components/VDiskStatus/VDiskStatus.scss +4 -0
  79. package/dist/components/VDiskStatus/statuses.js +2 -2
  80. package/dist/components/VDiskStatus/statuses.js.map +1 -1
  81. package/dist/components/nodesColumns/columns.d.ts +1 -0
  82. package/dist/components/nodesColumns/columns.js +13 -6
  83. package/dist/components/nodesColumns/columns.js.map +1 -1
  84. package/dist/containers/Clusters/Clusters.js +35 -6
  85. package/dist/containers/Clusters/Clusters.js.map +1 -1
  86. package/dist/containers/Clusters/Clusters.scss +24 -6
  87. package/dist/containers/Clusters/columns.js +9 -4
  88. package/dist/containers/Clusters/columns.js.map +1 -1
  89. package/dist/containers/Clusters/i18n/en.json +6 -0
  90. package/dist/containers/Clusters/i18n/index.d.ts +1 -1
  91. package/dist/containers/Header/Header.js +8 -0
  92. package/dist/containers/Header/Header.js.map +1 -1
  93. package/dist/containers/Header/HeaderLeftControls.js +3 -1
  94. package/dist/containers/Header/HeaderLeftControls.js.map +1 -1
  95. package/dist/containers/Header/PDiskLeftControls.d.ts +1 -0
  96. package/dist/containers/Header/PDiskLeftControls.js +24 -0
  97. package/dist/containers/Header/PDiskLeftControls.js.map +1 -0
  98. package/dist/containers/Header/VDiskLeftControls.d.ts +1 -0
  99. package/dist/containers/Header/VDiskLeftControls.js +23 -0
  100. package/dist/containers/Header/VDiskLeftControls.js.map +1 -0
  101. package/dist/containers/Header/breadcrumbs.js +5 -2
  102. package/dist/containers/Header/breadcrumbs.js.map +1 -1
  103. package/dist/containers/Header/i18n/en.json +4 -1
  104. package/dist/containers/Header/i18n/index.d.ts +1 -1
  105. package/dist/containers/Nodes/NodesControls/NodesControls.js +1 -1
  106. package/dist/containers/Nodes/NodesControls/NodesControls.js.map +1 -1
  107. package/dist/containers/Nodes/PaginatedNodes/GroupedNodesComponent.js +5 -3
  108. package/dist/containers/Nodes/PaginatedNodes/GroupedNodesComponent.js.map +1 -1
  109. package/dist/containers/Nodes/PaginatedNodes/NodesComponent.js +4 -2
  110. package/dist/containers/Nodes/PaginatedNodes/NodesComponent.js.map +1 -1
  111. package/dist/containers/Nodes/useDebouncedSearch.d.ts +2 -0
  112. package/dist/containers/Nodes/useDebouncedSearch.js +11 -0
  113. package/dist/containers/Nodes/useDebouncedSearch.js.map +1 -0
  114. package/dist/containers/PDiskPage/PDiskPage.js +30 -31
  115. package/dist/containers/PDiskPage/PDiskPage.js.map +1 -1
  116. package/dist/containers/PDiskPage/PDiskPage.scss +22 -11
  117. package/dist/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.js +3 -3
  118. package/dist/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.js.map +1 -1
  119. package/dist/containers/PDiskPage/PDiskStorageDetails.d.ts +6 -0
  120. package/dist/containers/PDiskPage/PDiskStorageDetails.js +28 -0
  121. package/dist/containers/PDiskPage/PDiskStorageDetails.js.map +1 -0
  122. package/dist/containers/PDiskPage/PDiskStorageDetails.scss +51 -0
  123. package/dist/containers/PDiskPage/i18n/en.json +2 -0
  124. package/dist/containers/PDiskPage/i18n/index.d.ts +1 -1
  125. package/dist/containers/Storage/PaginatedStorageGroupsTable/columns/i18n/index.d.ts +1 -1
  126. package/dist/containers/Storage/StorageExpertModePanel/constants.d.ts +1 -1
  127. package/dist/containers/Storage/StorageExpertModePanel/i18n/index.d.ts +1 -1
  128. package/dist/containers/Storage/useStorageQueryParams.d.ts +4 -4
  129. package/dist/containers/StorageGroupPage/StorageGroupPage.js +7 -5
  130. package/dist/containers/StorageGroupPage/StorageGroupPage.js.map +1 -1
  131. package/dist/containers/StorageGroupPage/StorageGroupPage.scss +10 -0
  132. package/dist/containers/StorageGroupPage/i18n/en.json +0 -1
  133. package/dist/containers/StorageGroupPage/i18n/index.d.ts +1 -1
  134. package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/ManagePartitioningDialog/i18n/index.d.ts +1 -1
  135. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +5 -12
  136. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js.map +1 -1
  137. package/dist/containers/Tenant/Diagnostics/TopQueries/columns/i18n/index.d.ts +1 -1
  138. package/dist/containers/Tenant/Healthcheck/Healthcheck.scss +6 -1
  139. package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssue.js +2 -2
  140. package/dist/containers/Tenant/Healthcheck/components/HealthcheckIssue.js.map +1 -1
  141. package/dist/containers/Tenant/TenantDrawerHealthcheck.js +10 -2
  142. package/dist/containers/Tenant/TenantDrawerHealthcheck.js.map +1 -1
  143. package/dist/containers/VDiskPage/VDiskPage.js +33 -33
  144. package/dist/containers/VDiskPage/VDiskPage.js.map +1 -1
  145. package/dist/containers/VDiskPage/VDiskPage.scss +21 -8
  146. package/dist/containers/VDiskPage/VDiskStorageDetails.js +27 -29
  147. package/dist/containers/VDiskPage/VDiskStorageDetails.js.map +1 -1
  148. package/dist/containers/VDiskPage/VDiskStorageDetails.scss +24 -93
  149. package/dist/containers/VDiskPage/i18n/en.json +3 -12
  150. package/dist/containers/VDiskPage/i18n/index.d.ts +1 -1
  151. package/dist/store/reducers/cluster/parseFields.js +1 -0
  152. package/dist/store/reducers/cluster/parseFields.js.map +1 -1
  153. package/dist/store/reducers/clusters/clusters.js +1 -0
  154. package/dist/store/reducers/clusters/clusters.js.map +1 -1
  155. package/dist/store/reducers/clusters/selectors.d.ts +1 -0
  156. package/dist/store/reducers/clusters/selectors.js +4 -1
  157. package/dist/store/reducers/clusters/selectors.js.map +1 -1
  158. package/dist/store/reducers/clusters/types.d.ts +2 -0
  159. package/dist/store/reducers/storage/prepareGroupsDisks.d.ts +1 -1
  160. package/dist/store/reducers/storage/prepareGroupsDisks.js +2 -1
  161. package/dist/store/reducers/storage/prepareGroupsDisks.js.map +1 -1
  162. package/dist/store/reducers/storage/utils.js +10 -3
  163. package/dist/store/reducers/storage/utils.js.map +1 -1
  164. package/dist/store/reducers/vdisk/types.d.ts +1 -1
  165. package/dist/store/reducers/vdisk/utils.js +5 -5
  166. package/dist/store/reducers/vdisk/utils.js.map +1 -1
  167. package/dist/types/api/meta.d.ts +1 -0
  168. package/dist/types/api/nodesList.d.ts +2 -0
  169. package/dist/utils/hooks/useDatabaseHealthcheckAvailable.d.ts +2 -0
  170. package/dist/utils/hooks/useDatabaseHealthcheckAvailable.js +6 -0
  171. package/dist/utils/hooks/useDatabaseHealthcheckAvailable.js.map +1 -0
  172. package/dist/utils/memory.d.ts +5 -0
  173. package/dist/utils/memory.js +9 -0
  174. package/dist/utils/memory.js.map +1 -0
  175. package/dist/utils/nodes.js +3 -3
  176. package/dist/utils/nodes.js.map +1 -1
  177. package/dist/utils/storageMetrics.d.ts +1 -0
  178. package/dist/utils/storageMetrics.js +8 -0
  179. package/dist/utils/storageMetrics.js.map +1 -1
  180. package/package.json +1 -1
  181. package/src/components/ButtonWithConfirmDialog/ButtonWithConfirmDialog.tsx +22 -3
  182. package/src/components/CopyableValue/CopyableValue.scss +16 -0
  183. package/src/components/CopyableValue/CopyableValue.tsx +40 -0
  184. package/src/components/CopyableValue/i18n/en.json +3 -0
  185. package/src/components/CopyableValue/i18n/index.ts +8 -0
  186. package/src/components/CriticalActionDialog/CriticalActionDialog.tsx +3 -0
  187. package/src/components/DiskCapacityInfo/DiskCapacityInfo.tsx +40 -185
  188. package/src/components/DiskCapacityInfo/i18n/en.json +0 -1
  189. package/src/components/EntityStatus/EntityStatus.scss +5 -0
  190. package/src/components/EntityStatus/EntityStatus.tsx +38 -5
  191. package/src/components/EvictVDiskButton/EvictVDiskButton.tsx +10 -2
  192. package/src/components/EvictVDiskButton/i18n/en.json +2 -2
  193. package/src/components/FullNodeViewer/FullNodeViewer.tsx +4 -9
  194. package/src/components/HoverPopup/HoverPopup.tsx +95 -19
  195. package/src/components/MemoryViewer/MemoryViewer.tsx +30 -17
  196. package/src/components/MemoryViewer/i18n/en.json +4 -3
  197. package/src/components/MemoryViewer/utils.ts +19 -12
  198. package/src/components/PDiskInfo/PDiskInfo.scss +15 -0
  199. package/src/components/PDiskInfo/PDiskInfo.tsx +51 -243
  200. package/src/components/PDiskInfo/PDiskLogSize.tsx +12 -8
  201. package/src/components/PDiskInfo/i18n/en.json +6 -3
  202. package/src/components/PDiskInfo/statuses.ts +16 -3
  203. package/src/components/Search/Search.tsx +5 -2
  204. package/src/components/StorageGroupInfo/StorageGroupInfo.tsx +8 -13
  205. package/src/components/VDisk/VDisk.tsx +3 -2
  206. package/src/components/VDiskInfo/VDiskInfo.scss +19 -3
  207. package/src/components/VDiskInfo/VDiskInfo.tsx +119 -341
  208. package/src/components/VDiskInfo/getVDiskDetails.tsx +24 -2
  209. package/src/components/VDiskInfo/i18n/en.json +4 -13
  210. package/src/components/VDiskPopup/VDiskPopup.tsx +16 -24
  211. package/src/components/VDiskPopup/i18n/en.json +0 -2
  212. package/src/components/VDiskStatus/VDiskReplicationStatus.tsx +1 -1
  213. package/src/components/VDiskStatus/VDiskStatus.scss +4 -0
  214. package/src/components/VDiskStatus/VDiskStatus.tsx +13 -2
  215. package/src/components/VDiskStatus/statuses.ts +2 -2
  216. package/src/components/nodesColumns/columns.tsx +26 -12
  217. package/src/containers/Clusters/Clusters.scss +24 -6
  218. package/src/containers/Clusters/Clusters.tsx +74 -5
  219. package/src/containers/Clusters/columns.tsx +36 -6
  220. package/src/containers/Clusters/i18n/en.json +6 -0
  221. package/src/containers/Header/Header.tsx +8 -0
  222. package/src/containers/Header/HeaderLeftControls.tsx +3 -1
  223. package/src/containers/Header/PDiskLeftControls.tsx +41 -0
  224. package/src/containers/Header/VDiskLeftControls.tsx +40 -0
  225. package/src/containers/Header/breadcrumbs.tsx +4 -2
  226. package/src/containers/Header/i18n/en.json +4 -1
  227. package/src/containers/Nodes/NodesControls/NodesControls.tsx +1 -0
  228. package/src/containers/Nodes/PaginatedNodes/GroupedNodesComponent.tsx +5 -3
  229. package/src/containers/Nodes/PaginatedNodes/NodesComponent.tsx +4 -2
  230. package/src/containers/Nodes/useDebouncedSearch.ts +13 -0
  231. package/src/containers/PDiskPage/PDiskPage.scss +22 -11
  232. package/src/containers/PDiskPage/PDiskPage.tsx +88 -56
  233. package/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx +3 -9
  234. package/src/containers/PDiskPage/PDiskStorageDetails.scss +51 -0
  235. package/src/containers/PDiskPage/PDiskStorageDetails.tsx +64 -0
  236. package/src/containers/PDiskPage/i18n/en.json +2 -0
  237. package/src/containers/StorageGroupPage/StorageGroupPage.scss +10 -0
  238. package/src/containers/StorageGroupPage/StorageGroupPage.tsx +37 -28
  239. package/src/containers/StorageGroupPage/i18n/en.json +0 -1
  240. package/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +5 -32
  241. package/src/containers/Tenant/Healthcheck/Healthcheck.scss +6 -1
  242. package/src/containers/Tenant/Healthcheck/components/HealthcheckIssue.tsx +18 -8
  243. package/src/containers/Tenant/TenantDrawerHealthcheck.tsx +14 -1
  244. package/src/containers/VDiskPage/VDiskPage.scss +21 -8
  245. package/src/containers/VDiskPage/VDiskPage.tsx +103 -64
  246. package/src/containers/VDiskPage/VDiskStorageDetails.scss +24 -93
  247. package/src/containers/VDiskPage/VDiskStorageDetails.tsx +50 -158
  248. package/src/containers/VDiskPage/i18n/en.json +3 -14
  249. package/src/store/reducers/cluster/parseFields.ts +1 -0
  250. package/src/store/reducers/clusters/clusters.ts +1 -0
  251. package/src/store/reducers/clusters/selectors.ts +4 -0
  252. package/src/store/reducers/clusters/types.ts +3 -0
  253. package/src/store/reducers/storage/prepareGroupsDisks.ts +2 -0
  254. package/src/store/reducers/storage/utils.ts +13 -3
  255. package/src/store/reducers/vdisk/types.ts +2 -1
  256. package/src/store/reducers/vdisk/utils.ts +4 -5
  257. package/src/types/api/meta.ts +1 -0
  258. package/src/types/api/nodesList.ts +3 -0
  259. package/src/utils/hooks/useDatabaseHealthcheckAvailable.ts +9 -0
  260. package/src/utils/memory.ts +16 -0
  261. package/src/utils/nodes.ts +3 -2
  262. package/src/utils/storageMetrics.ts +11 -0
  263. package/dist/components/VDiskInfo/getVDiskCapacityColumns.d.ts +0 -11
  264. package/dist/components/VDiskInfo/getVDiskCapacityColumns.js +0 -52
  265. package/dist/components/VDiskInfo/getVDiskCapacityColumns.js.map +0 -1
  266. package/src/components/VDiskInfo/getVDiskCapacityColumns.tsx +0 -83
@@ -0,0 +1,40 @@
1
+ import {ClipboardButton, Flex} from '@gravity-ui/uikit';
2
+ import {skipToken} from '@reduxjs/toolkit/query';
3
+ import {StringParam, useQueryParams} from 'use-query-params';
4
+
5
+ import {VDiskStateLabel} from '../../components/VDiskStatus';
6
+ import {vDiskApi} from '../../store/reducers/vdisk/vdisk';
7
+
8
+ import {b} from './constants';
9
+ import {headerKeyset} from './i18n';
10
+
11
+ export function VDiskLeftControls() {
12
+ const [{vDiskId, nodeId, database}] = useQueryParams({
13
+ vDiskId: StringParam,
14
+ nodeId: StringParam,
15
+ database: StringParam,
16
+ });
17
+ const {currentData} = vDiskApi.endpoints.getVDiskData.useQueryState(
18
+ vDiskId
19
+ ? {vDiskId, nodeId: nodeId ?? undefined, database: database ?? undefined}
20
+ : skipToken,
21
+ );
22
+
23
+ if (!vDiskId) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <Flex alignItems="center" gap={2} className={b('left-controls')}>
29
+ <ClipboardButton
30
+ text={vDiskId}
31
+ size="s"
32
+ view="flat-secondary"
33
+ color="secondary"
34
+ aria-label={headerKeyset('action_copy-vdisk-id')}
35
+ tooltipInitialText={headerKeyset('action_copy-vdisk-id')}
36
+ />
37
+ <VDiskStateLabel state={currentData?.VDiskState} size="xs" />
38
+ </Flex>
39
+ );
40
+ }
@@ -35,6 +35,7 @@ import {
35
35
  TENANT_PAGES_IDS,
36
36
  } from '../../store/reducers/tenant/constants';
37
37
  import {CLUSTER_DEFAULT_TITLE, UNBREAKABLE_GAP, getTabletLabel} from '../../utils/constants';
38
+ import {getPDiskId} from '../../utils/disks/helpers';
38
39
  import {TenantTabsGroups} from '../Tenant/TenantPages';
39
40
 
40
41
  import {headerKeyset} from './i18n';
@@ -205,8 +206,9 @@ const getPDiskBreadcrumbs: GetBreadcrumbs<PDiskBreadcrumbsOptions> = (options, q
205
206
  });
206
207
 
207
208
  let text = headerKeyset('breadcrumbs.pDisk');
208
- if (pDiskId) {
209
- text += ` ${pDiskId}`;
209
+ const diskId = getPDiskId({nodeId, pDiskId}) ?? pDiskId;
210
+ if (diskId) {
211
+ text += ` ${diskId}`;
210
212
  }
211
213
 
212
214
  const hasLink = pDiskId && nodeId;
@@ -26,5 +26,8 @@
26
26
  "title_monitoring": "Open in {{system}}",
27
27
  "action_copy-path": "Copy path",
28
28
  "action_copy-group-id": "Copy Storage Group ID",
29
- "action_copy-group-id-from-breadcrumb": "Copy Storage Group ID from breadcrumb"
29
+ "action_copy-group-id-from-breadcrumb": "Copy Storage Group ID from breadcrumb",
30
+ "action_copy-vdisk-id": "Copy VDisk ID",
31
+ "action_copy-pdisk-id": "Copy PDisk ID",
32
+ "action_copy-pdisk-id-from-breadcrumb": "Copy PDisk ID from breadcrumb"
30
33
  }
@@ -68,6 +68,7 @@ export function NodesControls({
68
68
  <React.Fragment>
69
69
  <Search
70
70
  tableFilter
71
+ debounce={0}
71
72
  onChange={handleSearchQueryChange}
72
73
  placeholder={i18n('controls_search-placeholder')}
73
74
  width={238}
@@ -22,6 +22,7 @@ import {NodesControls} from '../NodesControls/NodesControls';
22
22
  import {NodesTable} from '../NodesTable';
23
23
  import i18n from '../i18n';
24
24
  import {b} from '../shared';
25
+ import {useDebouncedSearch} from '../useDebouncedSearch';
25
26
  import {useNodesPageQueryParams} from '../useNodesPageQueryParams';
26
27
 
27
28
  interface NodeGroupProps {
@@ -123,6 +124,7 @@ export function GroupedNodesComponent({
123
124
  groupByParams,
124
125
  withPeerRoleFilter,
125
126
  );
127
+ const searchFilter = useDebouncedSearch(searchValue);
126
128
  const useMetaProxy = useClusterWithProxy();
127
129
  const [autoRefreshInterval] = useAutoRefreshInterval();
128
130
 
@@ -145,7 +147,7 @@ export function GroupedNodesComponent({
145
147
  {
146
148
  path: schemaPathParam,
147
149
  database,
148
- filter: searchValue.trim(),
150
+ filter: searchFilter.trim(),
149
151
  filter_peer_role: peerRoleFilter,
150
152
  group: groupByParam,
151
153
  limit: 0,
@@ -185,7 +187,7 @@ export function GroupedNodesComponent({
185
187
  path={path}
186
188
  databaseFullPath={databaseFullPath}
187
189
  database={database}
188
- searchValue={searchValue}
190
+ searchValue={searchFilter}
189
191
  peerRoleFilter={peerRoleFilter}
190
192
  groupByParam={groupByParam}
191
193
  columns={columnsToShow}
@@ -225,7 +227,7 @@ export function GroupedNodesComponent({
225
227
  table={renderGroups()}
226
228
  tableWrapperProps={{
227
229
  scrollContainerRef,
228
- scrollDependencies: [searchValue, groupByParam, tableGroups, peerRoleFilter],
230
+ scrollDependencies: [searchFilter, groupByParam, tableGroups, peerRoleFilter],
229
231
  loading: isLoading,
230
232
  className: b('groups-wrapper'),
231
233
  }}
@@ -11,6 +11,7 @@ import type {PreparedStorageNode} from '../../../store/reducers/storage/types';
11
11
  import type {NodesGroupByField} from '../../../types/api/nodes';
12
12
  import {useSelectedColumns} from '../../../utils/hooks/useSelectedColumns';
13
13
  import {NodesTable} from '../NodesTable';
14
+ import {useDebouncedSearch} from '../useDebouncedSearch';
14
15
  import {useNodesPageQueryParams} from '../useNodesPageQueryParams';
15
16
 
16
17
  import {NodesControlsWithTableState} from './NodesControlsWithTableState';
@@ -46,6 +47,7 @@ export function NodesComponent({
46
47
  groupByParams,
47
48
  withPeerRoleFilter,
48
49
  );
50
+ const searchFilter = useDebouncedSearch(searchValue);
49
51
  const viewerNodesHandlerHasGrouping = useViewerNodesHandlerHasGrouping();
50
52
 
51
53
  const {columnsToShow, columnsToSelect, setColumns} = useSelectedColumns(
@@ -78,7 +80,7 @@ export function NodesComponent({
78
80
  path={path}
79
81
  database={database}
80
82
  databaseFullPath={databaseFullPath}
81
- searchValue={searchValue}
83
+ searchValue={searchFilter}
82
84
  withProblems={withProblems}
83
85
  uptimeFilter={uptimeFilter}
84
86
  peerRoleFilter={peerRoleFilter}
@@ -89,7 +91,7 @@ export function NodesComponent({
89
91
  }
90
92
  tableWrapperProps={{
91
93
  scrollContainerRef,
92
- scrollDependencies: [searchValue, withProblems, uptimeFilter, peerRoleFilter],
94
+ scrollDependencies: [searchFilter, withProblems, uptimeFilter, peerRoleFilter],
93
95
  }}
94
96
  />
95
97
  );
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ /** Debounce the table filter, never the input or its URL value. */
4
+ export function useDebouncedSearch(searchValue: string) {
5
+ const [debouncedSearch, setDebouncedSearch] = React.useState(searchValue);
6
+
7
+ React.useEffect(() => {
8
+ const timer = window.setTimeout(() => setDebouncedSearch(searchValue), 200);
9
+ return () => window.clearTimeout(timer);
10
+ }, [searchValue]);
11
+
12
+ return debouncedSearch;
13
+ }
@@ -4,12 +4,14 @@
4
4
  position: relative;
5
5
 
6
6
  overflow: auto;
7
+ overflow-anchor: none;
7
8
 
8
9
  height: 100%;
9
- padding: 0 20px;
10
+ padding: 20px 20px 0;
10
11
 
11
12
  &__meta,
12
13
  &__title,
14
+ &__storage-details,
13
15
  &__info,
14
16
  &__controls,
15
17
  &__tabs {
@@ -19,8 +21,23 @@
19
21
  margin-bottom: 20px;
20
22
  }
21
23
 
22
- &__meta {
23
- margin-top: 20px;
24
+ &__heading {
25
+ margin: 0;
26
+ overflow-wrap: anywhere;
27
+ }
28
+
29
+ &__title-content {
30
+ flex: 1;
31
+
32
+ min-width: 0;
33
+ }
34
+
35
+ &__refresh-control {
36
+ flex-shrink: 0;
37
+ }
38
+
39
+ &__storage-details {
40
+ margin-bottom: var(--g-spacing-4);
24
41
  }
25
42
 
26
43
  &__tabs {
@@ -31,17 +48,11 @@
31
48
  padding: 20px 0;
32
49
  }
33
50
 
34
- &__title {
35
- display: flex;
36
- flex-direction: row;
37
- align-items: center;
38
- gap: var(--g-spacing-2);
39
- }
40
-
41
51
  &__controls {
42
52
  display: flex;
53
+ flex-wrap: wrap;
43
54
  align-items: center;
44
- gap: var(--g-spacing-2);
55
+ gap: var(--g-spacing-3);
45
56
  }
46
57
 
47
58
  &__tabs {
@@ -1,36 +1,45 @@
1
1
  import React from 'react';
2
2
 
3
- import {ArrowRotateLeft} from '@gravity-ui/icons';
4
- import {Icon, Tab, TabList, TabProvider} from '@gravity-ui/uikit';
3
+ import {ArrowLeft, ArrowRotateLeft, Wrench} from '@gravity-ui/icons';
4
+ import {Button, Flex, Icon, Tab, TabList, TabProvider, Text} from '@gravity-ui/uikit';
5
5
  import {skipToken} from '@reduxjs/toolkit/query';
6
6
  import {Helmet} from 'react-helmet-async';
7
+ import {useHistory} from 'react-router-dom';
7
8
  import {StringParam, useQueryParams} from 'use-query-params';
8
9
  import {z} from 'zod';
9
10
 
11
+ import {AutoRefreshControl} from '../../components/AutoRefreshControl/AutoRefreshControl';
10
12
  import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
11
- import {EntityPageTitle} from '../../components/EntityPageTitle/EntityPageTitle';
13
+ import {CopyableValue} from '../../components/CopyableValue/CopyableValue';
14
+ import {DiskStatusLabel, DiskTypeLabel} from '../../components/DiskStatus/DiskStatus';
12
15
  import {ResponseError} from '../../components/Errors/ResponseError';
13
16
  import {InfoViewerSkeleton} from '../../components/InfoViewerSkeleton/InfoViewerSkeleton';
14
17
  import {InternalLink} from '../../components/InternalLink/InternalLink';
18
+ import {LinkWithIcon} from '../../components/LinkWithIcon/LinkWithIcon';
15
19
  import {PDiskInfo} from '../../components/PDiskInfo/PDiskInfo';
16
- import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
20
+ import {
21
+ getPDiskDecommitLabel,
22
+ getPDiskDriveLabel,
23
+ getPDiskMaintenanceLabel,
24
+ getPDiskStateLabel,
25
+ } from '../../components/PDiskInfo/statuses';
17
26
  import {getPDiskPagePath} from '../../routes';
18
27
  import {api} from '../../store/reducers/api';
19
28
  import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
20
29
  import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
21
30
  import {pDiskApi} from '../../store/reducers/pdisk/pdisk';
22
- import type {EDecommitStatus} from '../../types/api/pdisk';
23
31
  import {valueIsDefined} from '../../utils';
24
32
  import {cn} from '../../utils/cn';
25
- import {getDataSeverityColor, getPDiskId} from '../../utils/disks/helpers';
33
+ import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
34
+ import {createPDiskDeveloperUILink, useHasDeveloperUi} from '../../utils/developerUI/developerUI';
35
+ import {getPDiskId} from '../../utils/disks/helpers';
26
36
  import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
27
37
  import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
28
38
  import {useAppTitle} from '../App/AppTitleContext';
29
39
  import {PaginatedStorage} from '../Storage/PaginatedStorage';
30
40
 
31
- import {DecommissionButton} from './DecommissionButton/DecommissionButton';
32
- import {DecommissionLabel} from './DecommissionLabel/DecommissionLabel';
33
41
  import {PDiskSpaceDistribution} from './PDiskSpaceDistribution/PDiskSpaceDistribution';
42
+ import {PDiskStorageDetails} from './PDiskStorageDetails';
34
43
  import {pDiskPageKeyset} from './i18n';
35
44
 
36
45
  import './PDiskPage.scss';
@@ -61,6 +70,9 @@ const pDiskTabSchema = z.nativeEnum(PDISK_TABS_IDS).catch(PDISK_TABS_IDS.spaceDi
61
70
 
62
71
  export function PDiskPage() {
63
72
  const dispatch = useTypedDispatch();
73
+ const history = useHistory();
74
+ const showBackButton = history.length > 1;
75
+ const hasDeveloperUi = useHasDeveloperUi();
64
76
 
65
77
  const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
66
78
  const newDiskApiAvailable = useDiskPagesAvailable();
@@ -87,7 +99,8 @@ export function PDiskPage() {
87
99
  });
88
100
  const pDiskLoading = pdiskDataQuery.isFetching && pdiskDataQuery.currentData === undefined;
89
101
  const pDiskData = pdiskDataQuery.currentData;
90
- const {NodeHost, NodeId, NodeType, NodeDC, Severity, DecommitStatus} = pDiskData || {};
102
+ const {NodeHost, DecommitStatus} = pDiskData || {};
103
+ const diskId = getPDiskId({nodeId, pDiskId});
91
104
 
92
105
  const handleRestart = async (isRetry?: boolean) => {
93
106
  if (pDiskParamsDefined) {
@@ -105,27 +118,6 @@ export function PDiskPage() {
105
118
  }
106
119
  };
107
120
 
108
- const handleDecommissionChange = async (
109
- newDecommissionStatus?: EDecommitStatus,
110
- isRetry?: boolean,
111
- ) => {
112
- if (pDiskParamsDefined) {
113
- const response = await window.api.pdisk.changePDiskStatus({
114
- nodeId,
115
- pDiskId,
116
- force: isRetry,
117
- decommissionStatus: newDecommissionStatus,
118
- });
119
- if (response?.result === false) {
120
- const err = {
121
- statusText: response.error,
122
- retryPossible: response.forceRetryPossible,
123
- };
124
- throw err;
125
- }
126
- }
127
- };
128
-
129
121
  const handleAfterAction = () => {
130
122
  if (pDiskParamsDefined) {
131
123
  dispatch(
@@ -153,39 +145,75 @@ export function PDiskPage() {
153
145
  };
154
146
 
155
147
  const renderPageMeta = () => {
156
- const hostItem = NodeHost ? `${pDiskPageKeyset('fqdn')}: ${NodeHost}` : undefined;
157
- const nodeIdItem = NodeId ? `${pDiskPageKeyset('node')}: ${NodeId}` : undefined;
158
-
148
+ if (!showBackButton) {
149
+ return null;
150
+ }
159
151
  return (
160
- <PageMetaWithAutorefresh
161
- loading={pDiskLoading}
162
- items={[hostItem, nodeIdItem, NodeType, NodeDC]}
152
+ <Flex
163
153
  className={pdiskPageCn('meta')}
164
- />
154
+ alignItems="center"
155
+ justifyContent="space-between"
156
+ gap={1}
157
+ >
158
+ <Button size="m" view="outlined" onClick={() => history.goBack()}>
159
+ <Icon data={ArrowLeft} size={16} />
160
+ {pDiskPageKeyset('action_back')}
161
+ </Button>
162
+ <AutoRefreshControl className={pdiskPageCn('refresh-control')} />
163
+ </Flex>
165
164
  );
166
165
  };
167
166
 
168
167
  const renderPageTitle = () => {
168
+ const statusLabels = [
169
+ {id: 'state', label: getPDiskStateLabel(pDiskData?.State)},
170
+ {id: 'drive', label: getPDiskDriveLabel(pDiskData?.DriveStatus)},
171
+ {id: 'decommit', label: getPDiskDecommitLabel(DecommitStatus)},
172
+ {id: 'maintenance', label: getPDiskMaintenanceLabel(pDiskData?.MaintenanceStatus)},
173
+ ];
169
174
  return (
170
- <div className={pdiskPageCn('title')}>
171
- <EntityPageTitle
172
- entityName={pDiskPageKeyset('pdisk')}
173
- status={getDataSeverityColor(Severity)}
174
- id={getPDiskId({nodeId, pDiskId})}
175
- />
176
- <DecommissionLabel decommission={DecommitStatus} />
177
- </div>
175
+ <Flex
176
+ className={pdiskPageCn('title')}
177
+ gap={2}
178
+ alignItems="flex-start"
179
+ qa="pdisk-header"
180
+ >
181
+ <Flex
182
+ className={pdiskPageCn('title-content')}
183
+ gap={3}
184
+ alignItems="center"
185
+ wrap="wrap"
186
+ >
187
+ <CopyableValue copyText={diskId} fieldName={pDiskPageKeyset('pdisk')}>
188
+ <Text as="h1" variant="header-1" className={pdiskPageCn('heading')}>
189
+ {pDiskPageKeyset('pdisk')}{' '}
190
+ <Text color="hint" variant="header-1">
191
+ {diskId || EMPTY_DATA_PLACEHOLDER}
192
+ </Text>
193
+ </Text>
194
+ </CopyableValue>
195
+ <Flex gap={2} alignItems="center" wrap="wrap">
196
+ {statusLabels.map(({id, label}) =>
197
+ label ? <DiskStatusLabel key={id} {...label} iconSize={14} /> : null,
198
+ )}
199
+ <DiskTypeLabel type={pDiskData?.Type} size="s" />
200
+ </Flex>
201
+ </Flex>
202
+ {!showBackButton && (
203
+ <AutoRefreshControl className={pdiskPageCn('refresh-control')} />
204
+ )}
205
+ </Flex>
178
206
  );
179
207
  };
180
208
 
181
209
  const renderControls = () => {
182
210
  return (
183
- <div className={pdiskPageCn('controls')}>
211
+ <div className={pdiskPageCn('controls')} data-qa="pdisk-controls">
184
212
  <ButtonWithConfirmDialog
185
213
  onConfirmAction={handleRestart}
186
214
  onConfirmActionSuccess={handleAfterAction}
187
215
  buttonDisabled={!pDiskParamsDefined || !isUserAllowedToMakeChanges}
188
- buttonView="normal"
216
+ buttonView="action"
189
217
  dialogHeader={pDiskPageKeyset('restart-pdisk-dialog-header')}
190
218
  dialogText={pDiskPageKeyset('restart-pdisk-dialog-text')}
191
219
  applyButtonText={pDiskPageKeyset('restart-pdisk-button')}
@@ -198,15 +226,14 @@ export function PDiskPage() {
198
226
  <Icon data={ArrowRotateLeft} />
199
227
  {pDiskPageKeyset('restart-pdisk-button')}
200
228
  </ButtonWithConfirmDialog>
201
- {newDiskApiAvailable ? (
202
- <DecommissionButton
203
- decommission={DecommitStatus}
204
- onConfirmAction={handleDecommissionChange}
205
- onConfirmActionSuccess={handleAfterAction}
206
- buttonDisabled={!pDiskParamsDefined || !isUserAllowedToMakeChanges}
207
- popoverDisabled={isUserAllowedToMakeChanges}
229
+ {hasDeveloperUi && pDiskParamsDefined && (
230
+ <LinkWithIcon
231
+ title={pDiskPageKeyset('action_open-in-developer-ui')}
232
+ url={createPDiskDeveloperUILink({nodeId, pDiskId})}
233
+ icon={Wrench}
234
+ hideEndIcon
208
235
  />
209
- ) : null}
236
+ )}
210
237
  </div>
211
238
  );
212
239
  };
@@ -215,7 +242,12 @@ export function PDiskPage() {
215
242
  if (pDiskLoading) {
216
243
  return <InfoViewerSkeleton className={pdiskPageCn('info')} rows={10} />;
217
244
  }
218
- return <PDiskInfo pDisk={pDiskData} nodeId={nodeId} className={pdiskPageCn('info')} />;
245
+ return (
246
+ <React.Fragment>
247
+ <PDiskStorageDetails data={pDiskData} className={pdiskPageCn('storage-details')} />
248
+ <PDiskInfo pDisk={pDiskData} className={pdiskPageCn('info')} />
249
+ </React.Fragment>
250
+ );
219
251
  };
220
252
 
221
253
  const renderTabs = () => {
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import {DiskStateProgressBar} from '../../../components/DiskStateProgressBar/DiskStateProgressBar';
4
4
  import {HoverPopup} from '../../../components/HoverPopup/HoverPopup';
5
5
  import {InternalLink} from '../../../components/InternalLink';
6
- import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
6
+ import {PDiskLogSize} from '../../../components/PDiskInfo/PDiskLogSize';
7
7
  import {VDiskPopup} from '../../../components/VDiskPopup/VDiskPopup';
8
8
  import type {YDBDefinitionListItem} from '../../../components/YDBDefinitionList/YDBDefinitionList';
9
9
  import {YDBDefinitionList} from '../../../components/YDBDefinitionList/YDBDefinitionList';
@@ -286,20 +286,14 @@ function LogInfo({data}: LogInfoProps) {
286
286
  const items: YDBDefinitionListItem[] = [
287
287
  {
288
288
  name: pDiskPageKeyset('label.log-size'),
289
- content: (
290
- <ProgressViewer
291
- value={LogUsedSize}
292
- capacity={LogTotalSize}
293
- formatValues={formatStorageValuesToGb}
294
- />
295
- ),
289
+ content: <PDiskLogSize used={LogUsedSize} total={LogTotalSize} />,
296
290
  },
297
291
  ];
298
292
 
299
293
  if (valueIsDefined(SystemSize)) {
300
294
  items.push({
301
295
  name: pDiskPageKeyset('label.system-size'),
302
- content: formatBytes({value: SystemSize}),
296
+ content: formatBytes({value: SystemSize, fixedDecimalPlaces: 2}),
303
297
  });
304
298
  }
305
299
 
@@ -0,0 +1,51 @@
1
+ .ydb-pdisk-storage-details {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ align-items: flex-start;
5
+ gap: var(--g-spacing-5);
6
+
7
+ width: fit-content;
8
+ max-width: 100%;
9
+ padding: var(--g-spacing-3) var(--g-spacing-5);
10
+
11
+ border-radius: var(--g-border-radius-m);
12
+ background-color: var(--g-color-base-generic);
13
+
14
+ &__detail {
15
+ --g-definition-list-item-gap: 0px;
16
+
17
+ min-width: 0;
18
+ max-width: 200px;
19
+
20
+ &_location {
21
+ min-width: 100px;
22
+ }
23
+
24
+ .g-definition-list__definition {
25
+ min-width: 0;
26
+ max-width: 100%;
27
+ }
28
+ }
29
+
30
+ &__value-popover {
31
+ flex: 0 1 auto;
32
+
33
+ min-width: 0;
34
+ max-width: 100%;
35
+ }
36
+
37
+ &__value-popover-content {
38
+ width: auto;
39
+ min-width: 0;
40
+ max-width: 100%;
41
+
42
+ &_with-left-trim {
43
+ text-align: end;
44
+ direction: rtl;
45
+
46
+ > span {
47
+ unicode-bidi: plaintext;
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,64 @@
1
+ import {DefinitionList} from '@gravity-ui/uikit';
2
+
3
+ import {CellWithPopover} from '../../components/CellWithPopover/CellWithPopover';
4
+ import {CopyableValue} from '../../components/CopyableValue/CopyableValue';
5
+ import type {PDiskData} from '../../store/reducers/pdisk/types';
6
+ import {cn} from '../../utils/cn';
7
+ import {getDiskLocationItems} from '../../utils/disks/diskInfo/getDiskLocationItems';
8
+
9
+ import './PDiskStorageDetails.scss';
10
+
11
+ const b = cn('ydb-pdisk-storage-details');
12
+ const LOCATION_IDS = ['datacenter', 'rack', 'fqdn', 'pdisk-path'];
13
+ const FIELD_ORDER = [...LOCATION_IDS, 'node-id', 'pdisk-id'];
14
+
15
+ export function PDiskStorageDetails({
16
+ data = {},
17
+ className,
18
+ }: {
19
+ data?: PDiskData;
20
+ className?: string;
21
+ }) {
22
+ const items = getDiskLocationItems(data, {
23
+ Host: data.NodeHost,
24
+ Rack: data.NodeRack,
25
+ DC: data.NodeDC,
26
+ });
27
+
28
+ return (
29
+ <div className={b(null, className)} data-qa="pdisk-location">
30
+ {FIELD_ORDER.map((id) => {
31
+ const item = items.find((entry) => entry.id === id);
32
+ if (!item) {
33
+ return null;
34
+ }
35
+ const copyText = item.copyText === undefined ? undefined : String(item.copyText);
36
+ return (
37
+ <DefinitionList
38
+ key={id}
39
+ direction="vertical"
40
+ className={b('detail', {location: LOCATION_IDS.includes(id)})}
41
+ qa={`pdisk-location-${id}`}
42
+ >
43
+ <DefinitionList.Item name={item.name}>
44
+ <CopyableValue copyText={copyText} fieldName={item.name}>
45
+ <CellWithPopover
46
+ content={copyText}
47
+ disabled={!copyText}
48
+ placement={['top', 'bottom']}
49
+ fullWidth
50
+ wrapperClassName={b('value-popover')}
51
+ className={b('value-popover-content', {
52
+ 'with-left-trim': id === 'pdisk-path',
53
+ })}
54
+ >
55
+ <span tabIndex={copyText ? 0 : undefined}>{item.content}</span>
56
+ </CellWithPopover>
57
+ </CopyableValue>
58
+ </DefinitionList.Item>
59
+ </DefinitionList>
60
+ );
61
+ })}
62
+ </div>
63
+ );
64
+ }
@@ -2,6 +2,8 @@
2
2
  "fqdn": "FQDN",
3
3
  "pdisk": "PDisk",
4
4
  "node": "Node",
5
+ "action_back": "Back",
6
+ "action_open-in-developer-ui": "Open in Developer UI",
5
7
 
6
8
  "storage": "Storage",
7
9
  "space-distribution": "Space distribution",
@@ -27,6 +27,16 @@
27
27
  margin-bottom: var(--g-spacing-4);
28
28
  }
29
29
 
30
+ &__title-content {
31
+ flex: 1;
32
+
33
+ min-width: 0;
34
+ }
35
+
36
+ &__refresh-control {
37
+ flex-shrink: 0;
38
+ }
39
+
30
40
  &__storage-title {
31
41
  margin: 0;
32
42
  }