ydb-embedded-ui 3.4.0 → 3.4.2

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 (70) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +2 -3
  3. package/dist/components/ClusterInfo/ClusterInfo.tsx +14 -36
  4. package/dist/components/CopyToClipboard/CopyToClipboard.tsx +1 -2
  5. package/dist/components/CriticalActionDialog/CriticalActionDialog.js +1 -1
  6. package/dist/components/EmptyState/{EmptyState.js → EmptyState.tsx} +12 -15
  7. package/dist/components/EmptyState/index.ts +1 -0
  8. package/dist/components/EnableFullscreenButton/EnableFullscreenButton.tsx +7 -2
  9. package/dist/components/Errors/403/AccessDenied.tsx +1 -1
  10. package/dist/components/Fullscreen/Fullscreen.tsx +1 -1
  11. package/dist/components/Icon/Icon.tsx +33 -0
  12. package/dist/components/Icon/index.ts +1 -0
  13. package/dist/components/Pagination/Pagination.js +1 -1
  14. package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +1 -1
  15. package/dist/components/Tablet/Tablet.tsx +59 -0
  16. package/dist/components/Tablet/index.ts +1 -0
  17. package/dist/components/Tag/Tag.tsx +16 -0
  18. package/dist/components/Tag/index.ts +1 -0
  19. package/dist/components/Tags/Tags.tsx +22 -0
  20. package/dist/components/Tags/index.ts +1 -0
  21. package/dist/containers/Header/Header.tsx +2 -3
  22. package/dist/containers/Header/Host/Host.js +1 -1
  23. package/dist/containers/Node/NodeStructure/Pdisk.tsx +1 -1
  24. package/dist/containers/Nodes/getNodesColumns.tsx +1 -1
  25. package/dist/containers/Storage/EmptyFilter/EmptyFilter.tsx +8 -9
  26. package/dist/containers/Storage/PDisk/PDisk.scss +15 -0
  27. package/dist/containers/Storage/PDisk/PDisk.tsx +38 -13
  28. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +4 -2
  29. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -0
  30. package/dist/containers/Tablet/Tablet.js +6 -6
  31. package/dist/containers/Tablets/Tablets.scss +0 -4
  32. package/dist/containers/Tablets/Tablets.tsx +1 -2
  33. package/dist/containers/TabletsFilters/TabletsFilters.js +1 -1
  34. package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +1 -1
  35. package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +27 -1
  36. package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +1 -2
  37. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
  38. package/dist/containers/Tenant/Diagnostics/Network/Network.js +1 -1
  39. package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +17 -13
  40. package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +2 -0
  41. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +1 -1
  42. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +1 -1
  43. package/dist/containers/Tenant/Preview/Preview.js +1 -1
  44. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +1 -1
  45. package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.js +1 -1
  46. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
  47. package/dist/containers/Tenant/TenantPages.tsx +1 -2
  48. package/dist/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx +2 -2
  49. package/dist/services/api.d.ts +11 -0
  50. package/dist/services/api.js +3 -3
  51. package/dist/store/reducers/consumer.ts +14 -0
  52. package/dist/store/reducers/{host.js → host.ts} +9 -5
  53. package/dist/store/reducers/shardsWorkload.ts +4 -0
  54. package/dist/store/reducers/tablet.ts +111 -0
  55. package/dist/store/reducers/topic.ts +13 -0
  56. package/dist/store/state-url-mapping.js +3 -0
  57. package/dist/types/api/cluster.ts +34 -0
  58. package/dist/types/api/systemState.ts +13 -0
  59. package/dist/types/api/tablet.ts +12 -4
  60. package/dist/types/store/consumer.ts +10 -2
  61. package/dist/types/store/host.ts +23 -0
  62. package/dist/types/store/tablet.ts +50 -0
  63. package/dist/types/store/tooltip.ts +3 -1
  64. package/dist/types/store/topic.ts +3 -2
  65. package/package.json +7 -5
  66. package/dist/components/Icon/Icon.js +0 -28
  67. package/dist/components/Tablet/Tablet.js +0 -61
  68. package/dist/components/Tag/Tag.js +0 -14
  69. package/dist/components/Tags/Tags.js +0 -36
  70. package/dist/store/reducers/tablet.js +0 -94
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
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
+
12
+ ## [3.4.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.0...v3.4.1) (2023-03-01)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **Consumers:** enable navigation to Partitions tab ([fa79081](https://github.com/ydb-platform/ydb-embedded-ui/commit/fa7908124bc4392e272aa829fd4e5c1639fcf209))
18
+ * **Consumers:** update topic stats values align ([f2af851](https://github.com/ydb-platform/ydb-embedded-ui/commit/f2af851208a640ef9aa392fd7176eb579a2401db))
19
+ * **TopShards:** keep state on request cancel ([1bd4f65](https://github.com/ydb-platform/ydb-embedded-ui/commit/1bd4f65dd047b42f8edf9e4bb41c722f30220d77))
20
+
3
21
  ## [3.4.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.3.4...v3.4.0) (2023-02-17)
4
22
 
5
23
 
@@ -1,8 +1,8 @@
1
1
  import cn from 'bem-cn-lite';
2
2
 
3
3
  import EntityStatus from '../EntityStatus/EntityStatus';
4
- import Tags from '../Tags/Tags';
5
- import Icon from '../Icon/Icon';
4
+ import {Tags} from '../Tags';
5
+ import {Icon} from '../Icon';
6
6
 
7
7
  import './BasicNodeViewer.scss';
8
8
 
@@ -47,7 +47,6 @@ export const BasicNodeViewer = ({node, additionalNodesInfo, className}: BasicNod
47
47
  ) : (
48
48
  <div className="error">no data</div>
49
49
  )}
50
-
51
50
  </div>
52
51
  );
53
52
  };
@@ -1,6 +1,7 @@
1
1
  import React, {ReactNode} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
+ import {Link, Loader} from '@gravity-ui/uikit';
4
5
 
5
6
  //@ts-ignore
6
7
  import EntityStatus from '../EntityStatus/EntityStatus';
@@ -8,10 +9,8 @@ import EntityStatus from '../EntityStatus/EntityStatus';
8
9
  import ProgressViewer from '../ProgressViewer/ProgressViewer';
9
10
  //@ts-ignore
10
11
  import InfoViewer from '../InfoViewer/InfoViewer';
11
- //@ts-ignore
12
- import Tags from '../Tags/Tags';
13
- //@ts-ignore
14
- import Tablet from '../Tablet/Tablet';
12
+ import {Tags} from '../Tags';
13
+ import {Tablet} from '../Tablet';
15
14
 
16
15
  //@ts-ignore
17
16
  import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
@@ -24,15 +23,16 @@ import {clusterName, backend, customBackend} from '../../store';
24
23
  import {formatStorageValues} from '../../utils';
25
24
  //@ts-ignore
26
25
  import {TxAllocator} from '../../utils/constants';
27
-
28
- import './ClusterInfo.scss';
29
26
  import {AutoFetcher} from '../../utils/autofetcher';
30
- import {Link, Loader} from '@gravity-ui/uikit';
31
- //@ts-ignore
32
- import Icon from '../Icon/Icon';
27
+ import {Icon} from '../Icon';
33
28
  import {setHeader} from '../../store/reducers/header';
34
29
  import routes, {CLUSTER_PAGES, createHref} from '../../routes';
35
30
 
31
+ import type {TClusterInfo} from '../../types/api/cluster';
32
+ import type {TTabletStateInfo} from '../../types/api/tablet';
33
+
34
+ import './ClusterInfo.scss';
35
+
36
36
  const b = cn('cluster-info');
37
37
 
38
38
  export interface IClusterInfoItem {
@@ -40,25 +40,11 @@ export interface IClusterInfoItem {
40
40
  value: ReactNode;
41
41
  }
42
42
 
43
- interface ICluster {
44
- StorageTotal: string;
45
- StorageUsed: string;
46
- NodesAlive: number;
47
- NodesTotal: number;
48
- LoadAverage: number;
49
- NumberOfCpus: number;
50
- Versions: string[];
51
- Name?: string;
52
- Overall: string;
53
- DataCenters?: string[];
54
- SystemTablets?: ITablet[];
55
- }
56
-
57
43
  interface ClusterInfoProps {
58
44
  className?: string;
59
- cluster?: ICluster;
45
+ cluster?: TClusterInfo;
60
46
  hideTooltip: VoidFunction;
61
- showTooltip: Function;
47
+ showTooltip: (...args: Parameters<typeof showTooltip>) => void;
62
48
  setHeader: any;
63
49
  getClusterInfo: (clusterName: string) => void;
64
50
  clusterTitle?: string;
@@ -69,12 +55,8 @@ interface ClusterInfoProps {
69
55
  error?: {statusText: string};
70
56
  }
71
57
 
72
- interface ITablet {
73
- Type: string;
74
- }
75
-
76
58
  class ClusterInfo extends React.Component<ClusterInfoProps> {
77
- static compareTablets(tablet1: ITablet, tablet2: ITablet) {
59
+ static compareTablets(tablet1: TTabletStateInfo, tablet2: TTabletStateInfo) {
78
60
  if (tablet1.Type === TxAllocator) {
79
61
  return 1;
80
62
  }
@@ -128,11 +110,7 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
128
110
  private autofetcher: any;
129
111
 
130
112
  private getInfo() {
131
- const {
132
- cluster = {} as ICluster,
133
- additionalClusterInfo = [],
134
- singleClusterMode,
135
- } = this.props;
113
+ const {cluster = {}, additionalClusterInfo = [], singleClusterMode} = this.props;
136
114
  const {StorageTotal, StorageUsed} = cluster;
137
115
 
138
116
  let link = backend + '/internal';
@@ -193,7 +171,7 @@ class ClusterInfo extends React.Component<ClusterInfoProps> {
193
171
 
194
172
  private renderContent = () => {
195
173
  const {
196
- cluster = {} as ICluster,
174
+ cluster = {},
197
175
  showTooltip,
198
176
  hideTooltip,
199
177
  clusterTitle,
@@ -1,7 +1,6 @@
1
1
  import {Button, CopyToClipboard as CopyToClipboardUiKit} from '@gravity-ui/uikit';
2
2
  import createToast from '../../utils/createToast';
3
- //@ts-ignore
4
- import Icon from '../Icon/Icon';
3
+ import {Icon} from '../Icon';
5
4
 
6
5
  interface CopyToClipboardProps {
7
6
  text: string;
@@ -2,7 +2,7 @@ import {useState} from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Dialog} from '@gravity-ui/uikit';
5
- import Icon from '../Icon/Icon';
5
+ import {Icon} from '../Icon';
6
6
 
7
7
  import './CriticalActionDialog.scss';
8
8
 
@@ -1,6 +1,7 @@
1
- import PropTypes from 'prop-types';
1
+ import {ReactNode} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
- import Icon from '../Icon/Icon';
3
+
4
+ import {Icon} from '../Icon';
4
5
 
5
6
  import './EmptyState.scss';
6
7
 
@@ -12,7 +13,15 @@ const sizes = {
12
13
  l: 350,
13
14
  };
14
15
 
15
- export default function EmptyState({image, title, description, actions, size}) {
16
+ interface EmptyStateProps {
17
+ title: string;
18
+ image?: ReactNode;
19
+ description?: ReactNode;
20
+ actions?: ReactNode[];
21
+ size?: keyof typeof sizes;
22
+ }
23
+
24
+ export const EmptyState = ({image, title, description, actions, size = 'm'}: EmptyStateProps) => {
16
25
  return (
17
26
  <div className={block({size})}>
18
27
  <div className={block('wrapper', {size})}>
@@ -35,16 +44,4 @@ export default function EmptyState({image, title, description, actions, size}) {
35
44
  </div>
36
45
  </div>
37
46
  );
38
- }
39
-
40
- EmptyState.propTypes = {
41
- title: PropTypes.string.isRequired,
42
- image: PropTypes.node,
43
- description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
44
- actions: PropTypes.arrayOf(PropTypes.node),
45
- size: PropTypes.string,
46
- };
47
-
48
- EmptyState.defaultProps = {
49
- size: 'm',
50
47
  };
@@ -0,0 +1 @@
1
+ export * from './EmptyState';
@@ -1,7 +1,7 @@
1
1
  import {Button} from '@gravity-ui/uikit';
2
2
  import {useDispatch} from 'react-redux';
3
3
  import {enableFullscreen} from '../../store/reducers/fullscreen';
4
- import Icon from '../Icon/Icon';
4
+ import {Icon} from '../Icon';
5
5
 
6
6
  interface EnableFullscreenButtonProps {
7
7
  disabled?: boolean;
@@ -13,7 +13,12 @@ function EnableFullscreenButton({disabled}: EnableFullscreenButtonProps) {
13
13
  dispatch(enableFullscreen());
14
14
  };
15
15
  return (
16
- <Button onClick={onEnableFullscreen} view="flat-secondary" disabled={disabled} title='Fullscreen'>
16
+ <Button
17
+ onClick={onEnableFullscreen}
18
+ view="flat-secondary"
19
+ disabled={disabled}
20
+ title="Fullscreen"
21
+ >
17
22
  <Icon name="enableFullscreen" viewBox={'0 0 16 16'} height={16} width={16} />
18
23
  </Button>
19
24
  );
@@ -1,4 +1,4 @@
1
- import EmptyState from '../../EmptyState/EmptyState';
1
+ import {EmptyState} from '../../EmptyState';
2
2
  import {Illustration} from '../../Illustration';
3
3
 
4
4
  import i18n from '../i18n';
@@ -6,7 +6,7 @@ import cn from 'bem-cn-lite';
6
6
 
7
7
  import {useDispatch} from 'react-redux';
8
8
  import {disableFullscreen} from '../../store/reducers/fullscreen';
9
- import Icon from '../Icon/Icon';
9
+ import {Icon} from '../Icon';
10
10
 
11
11
  import './Fullscreen.scss';
12
12
 
@@ -0,0 +1,33 @@
1
+ import {Icon as UiKitIcon} from '@gravity-ui/uikit';
2
+
3
+ interface IconProps {
4
+ name: string;
5
+ height?: number | string;
6
+ width?: number | string;
7
+ viewBox?: string;
8
+ className?: string;
9
+ onClick?: (event: React.MouseEvent<SVGElement, MouseEvent>) => void;
10
+ }
11
+
12
+ export const Icon = ({
13
+ name,
14
+ height = 16,
15
+ width = 16,
16
+ viewBox = '0 0 16 16',
17
+ className,
18
+ onClick,
19
+ }: IconProps) => {
20
+ return (
21
+ <UiKitIcon
22
+ // @ts-expect-error
23
+ // Both url and id strings are required in this uikit component.
24
+ // However, if no url provided, component uses id, which is what we need.
25
+ // If it is fixed in uikit it could be safely removed
26
+ data={{id: `icon.${name}`, viewBox}}
27
+ height={height}
28
+ width={width}
29
+ className={className}
30
+ onClick={onClick}
31
+ />
32
+ );
33
+ };
@@ -0,0 +1 @@
1
+ export * from './Icon';
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
4
  import {Button} from '@gravity-ui/uikit';
5
- import Icon from '../Icon/Icon';
5
+ import {Icon} from '../Icon';
6
6
  import Hotkey from '../Hotkey/Hotkey';
7
7
 
8
8
  import './Pagination.scss';
@@ -4,7 +4,7 @@ import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Icon as UiKitIcon} from '@gravity-ui/uikit';
6
6
 
7
- import Icon from '../Icon/Icon';
7
+ import {Icon} from '../Icon';
8
8
 
9
9
  import questionIcon from '../../assets/icons/question.svg';
10
10
 
@@ -0,0 +1,59 @@
1
+ import {useRef} from 'react';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import type {TTabletStateInfo} from '../../types/api/tablet';
5
+ import type {ShowTooltipFunction} from '../../types/store/tooltip';
6
+ import {getTabletLabel} from '../../utils/constants';
7
+ import routes, {createHref} from '../../routes';
8
+
9
+ import {InternalLink} from '../InternalLink';
10
+
11
+ import './Tablet.scss';
12
+
13
+ const b = cn('tablet');
14
+
15
+ interface TabletProps {
16
+ tablet?: TTabletStateInfo;
17
+ onMouseEnter?: (...args: Parameters<ShowTooltipFunction>) => void;
18
+ onMouseLeave?: VoidFunction;
19
+ }
20
+
21
+ export const Tablet = ({
22
+ tablet = {},
23
+ onMouseEnter = () => {},
24
+ onMouseLeave = () => {},
25
+ }: TabletProps) => {
26
+ const ref = useRef(null);
27
+
28
+ const _onTabletMouseEnter = () => {
29
+ onMouseEnter(ref.current, tablet, 'tablet');
30
+ };
31
+
32
+ const _onTabletClick = () => {
33
+ const {TabletId: id} = tablet;
34
+
35
+ if (id) {
36
+ onMouseLeave();
37
+ }
38
+ };
39
+
40
+ const {TabletId: id} = tablet;
41
+ const status = tablet.Overall?.toLowerCase();
42
+
43
+ return (
44
+ <InternalLink
45
+ onClick={_onTabletClick}
46
+ to={id && createHref(routes.tablet, {id})}
47
+ className={b('wrapper')}
48
+ >
49
+ <div
50
+ ref={ref}
51
+ className={b({status})}
52
+ onMouseEnter={_onTabletMouseEnter}
53
+ onMouseLeave={onMouseLeave}
54
+ >
55
+ <div className={b('type')}>{[getTabletLabel(tablet.Type)]}</div>
56
+ </div>
57
+ </InternalLink>
58
+ );
59
+ };
@@ -0,0 +1 @@
1
+ export * from './Tablet';
@@ -0,0 +1,16 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import './Tag.scss';
4
+
5
+ const b = cn('tag');
6
+
7
+ export type TagType = 'blue';
8
+
9
+ interface TagProps {
10
+ text: string;
11
+ type?: TagType;
12
+ }
13
+
14
+ export const Tag = ({text, type}: TagProps) => {
15
+ return <div className={b({type})}>{text}</div>;
16
+ };
@@ -0,0 +1 @@
1
+ export * from './Tag';
@@ -0,0 +1,22 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import {Tag, TagType} from '../Tag';
4
+
5
+ import './Tags.scss';
6
+
7
+ const b = cn('tags');
8
+
9
+ interface TagsProps {
10
+ tags: string[];
11
+ tagsType?: TagType;
12
+ className?: string;
13
+ }
14
+
15
+ export const Tags = ({tags, tagsType, className = ''}: TagsProps) => {
16
+ return (
17
+ <div className={b(null, className)}>
18
+ {tags &&
19
+ tags.map((tag, tagIndex) => <Tag text={tag} key={tagIndex} type={tagsType}></Tag>)}
20
+ </div>
21
+ );
22
+ };
@@ -0,0 +1 @@
1
+ export * from './Tags';
@@ -5,8 +5,7 @@ import {useHistory, useLocation} from 'react-router';
5
5
  import {Breadcrumbs, BreadcrumbsItem, Link} from '@gravity-ui/uikit';
6
6
 
7
7
  import Divider from '../../components/Divider/Divider';
8
- //@ts-ignore
9
- import Icon from '../../components/Icon/Icon';
8
+ import {Icon} from '../../components/Icon';
10
9
 
11
10
  import {clusterName as clusterNameLocation, backend, customBackend} from '../../store';
12
11
  import {getClusterInfo} from '../../store/reducers/cluster';
@@ -27,7 +26,7 @@ function ClusterName({name}: {name: string}) {
27
26
  }
28
27
 
29
28
  interface HeaderProps {
30
- clusterName: string
29
+ clusterName: string;
31
30
  }
32
31
 
33
32
  function Header({clusterName}: HeaderProps) {
@@ -5,7 +5,7 @@ import cn from 'bem-cn-lite';
5
5
  import {Link as ExternalLink} from '@gravity-ui/uikit';
6
6
 
7
7
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
8
- import {Tag} from '../../../components/Tag/Tag';
8
+ import {Tag} from '../../../components/Tag';
9
9
 
10
10
  import {calcUptime} from '../../../utils';
11
11
  import {customBackend} from '../../../store';
@@ -9,7 +9,7 @@ import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
9
9
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
10
10
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
11
11
  import ProgressViewer from '../../../components/ProgressViewer/ProgressViewer';
12
- import Icon from '../../../components/Icon/Icon';
12
+ import {Icon} from '../../../components/Icon';
13
13
  import {Vdisk} from './Vdisk';
14
14
 
15
15
  import {bytesToGB, pad9} from '../../../utils/utils';
@@ -2,7 +2,7 @@ import cn from 'bem-cn-lite';
2
2
  import DataTable, {Column} from '@gravity-ui/react-data-table';
3
3
  import {Button, Popover} from '@gravity-ui/uikit';
4
4
 
5
- import Icon from '../../components/Icon/Icon';
5
+ import {Icon} from '../../components/Icon';
6
6
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
7
7
  import PoolsGraph from '../../components/PoolsGraph/PoolsGraph';
8
8
  import ProgressViewer from '../../components/ProgressViewer/ProgressViewer';
@@ -1,6 +1,6 @@
1
1
  import {Button} from '@gravity-ui/uikit';
2
2
 
3
- import EmptyState from '../../../components/EmptyState/EmptyState';
3
+ import {EmptyState} from '../../../components/EmptyState';
4
4
  import {Illustration} from '../../../components/Illustration';
5
5
 
6
6
  import i18n from './i18n';
@@ -22,13 +22,12 @@ export const EmptyFilter = ({
22
22
  image={<Illustration name="thumbsUp" />}
23
23
  title={title}
24
24
  description={message}
25
- actions={onShowAll && [
26
- <Button
27
- key="show-all"
28
- onClick={onShowAll}
29
- >
30
- {showAll}
31
- </Button>
32
- ]}
25
+ actions={
26
+ onShowAll && [
27
+ <Button key="show-all" onClick={onShowAll}>
28
+ {showAll}
29
+ </Button>,
30
+ ]
31
+ }
33
32
  />
34
33
  );
@@ -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')}>
@@ -12,9 +12,9 @@ import '../../services/api';
12
12
 
13
13
  import InfoViewer from '../../components/InfoViewer/InfoViewer';
14
14
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
15
- import {Tag} from '../../components/Tag/Tag';
16
- import Icon from '../../components/Icon/Icon';
17
- import EmptyState from '../../components/EmptyState/EmptyState';
15
+ import {Tag} from '../../components/Tag';
16
+ import {Icon} from '../../components/Icon';
17
+ import {EmptyState} from '../../components/EmptyState';
18
18
  import {Link as ExternalLink, Button, Loader} from '@gravity-ui/uikit';
19
19
  import DataTable from '@gravity-ui/react-data-table';
20
20
  import CriticalActionDialog from '../../components/CriticalActionDialog/CriticalActionDialog';
@@ -111,9 +111,9 @@ class Tablet extends React.Component {
111
111
  const {isFirstFetchData} = this.state;
112
112
 
113
113
  if (version && this.isValidVersion()) {
114
- this.props.getTablet(id).then(({tablet}) => {
115
- if (isFirstFetchData && tablet.TenantId) {
116
- this.props.getTabletDescribe(tablet.TenantId);
114
+ this.props.getTablet(id).then(({tabletData}) => {
115
+ if (isFirstFetchData && tabletData.TenantId) {
116
+ this.props.getTabletDescribe(tabletData.TenantId);
117
117
  }
118
118
 
119
119
  this.setState({isFirstFetchData: false});