Skip to content

Commit 0859c37

Browse files
feat(ui): update Disk indicators visual design across console (#3142)
1 parent 25dff18 commit 0859c37

File tree

31 files changed

+469
-199
lines changed

31 files changed

+469
-199
lines changed

src/components/DiskStateProgressBar/DiskStateProgressBar.scss

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
$block: &;
55

66
$border-width: 1px;
7-
$outer-border-radius: 4px;
7+
$outer-border-radius: var(--g-border-radius-s);
88
$inner-border-radius: $outer-border-radius - $border-width;
9+
$outer-compact-border-radius: var(--g-border-radius-xs);
10+
$inner-compact-border-radius: $outer-compact-border-radius - $border-width;
911

10-
--progress-bar-full-height: var(--g-text-body-3-line-height);
12+
--progress-bar-full-height: var(--g-text-subheader-2-line-height);
1113
--progress-bar-compact-height: 12px;
1214

1315
--stripe-width: 4px;
@@ -16,6 +18,8 @@
1618
position: relative;
1719
z-index: 0;
1820

21+
overflow: hidden;
22+
1923
min-width: 50px;
2024
height: var(--progress-bar-full-height);
2125

@@ -25,13 +29,16 @@
2529
border: $border-width solid var(--entity-state-border-color);
2630
border-radius: $outer-border-radius;
2731
background-color: var(--entity-state-background-color);
28-
@include mixins.entity-state-colors();
32+
33+
transition: opacity 300ms ease-in-out;
34+
35+
@include mixins.entity-state-colors($block);
2936

3037
&_compact {
31-
min-width: 0;
38+
min-width: 8px;
3239
height: var(--progress-bar-compact-height);
3340

34-
border-radius: 2px;
41+
border-radius: $outer-compact-border-radius;
3542
}
3643

3744
&_faded {
@@ -42,6 +49,10 @@
4249
opacity: 0.5;
4350
}
4451

52+
&_darkened {
53+
opacity: 0.8;
54+
}
55+
4556
&_empty {
4657
color: var(--g-color-text-hint);
4758
border-style: dashed;
@@ -78,7 +89,7 @@
7889
}
7990

8091
&_compact {
81-
border-radius: 1px;
92+
border-radius: $inner-compact-border-radius;
8293
}
8394

8495
&_inverted {
@@ -93,20 +104,21 @@
93104
position: relative;
94105
z-index: 2;
95106

96-
margin-right: var(--g-spacing-1);
107+
margin-right: var(--g-spacing-half);
97108

98-
font-size: var(--g-text-body-1-font-size);
109+
font-family: var(--g-text-caption-font-family);
110+
font-size: var(--g-text-caption-1-font-size);
99111
// bar height minus borders
100112
line-height: calc(var(--progress-bar-full-height) - #{$border-width * 2});
101113

102-
color: inherit;
114+
color: var(--entity-state-font-color);
103115
}
104116

105117
&__icon {
106118
position: relative;
107119
z-index: 2;
108120

109-
margin-left: var(--g-spacing-1);
121+
margin-left: calc(var(--g-spacing-1) - $border-width);
110122

111123
color: var(--entity-state-border-color);
112124
}

src/components/DiskStateProgressBar/DiskStateProgressBar.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {cn} from '../../utils/cn';
77
import {DONOR_COLOR} from '../../utils/disks/constants';
88
import {getSeverityColor, getVDiskStatusIcon} from '../../utils/disks/helpers';
99
import {useSetting} from '../../utils/hooks';
10+
import {isNumeric} from '../../utils/utils';
1011

1112
import './DiskStateProgressBar.scss';
1213

@@ -24,6 +25,9 @@ interface DiskStateProgressBarProps {
2425
className?: string;
2526
isDonor?: boolean;
2627
withIcon?: boolean;
28+
highlighted?: boolean;
29+
darkened?: boolean;
30+
noDataPlaceholder?: React.ReactNode;
2731
}
2832

2933
export function DiskStateProgressBar({
@@ -38,6 +42,9 @@ export function DiskStateProgressBar({
3842
className,
3943
isDonor,
4044
withIcon,
45+
highlighted,
46+
darkened,
47+
noDataPlaceholder,
4148
}: DiskStateProgressBarProps) {
4249
const [inverted] = useSetting<boolean | undefined>(SETTING_KEYS.INVERTED_DISKS);
4350

@@ -48,6 +55,8 @@ export function DiskStateProgressBar({
4855
empty,
4956
inactive,
5057
striped,
58+
highlighted,
59+
darkened,
5160
};
5261

5362
if (isDonor) {
@@ -59,33 +68,39 @@ export function DiskStateProgressBar({
5968
}
6069
}
6170

71+
const hasAllocatedPercent = isNumeric(diskAllocatedPercent) && diskAllocatedPercent >= 0;
72+
6273
const renderAllocatedPercent = () => {
6374
if (compact) {
6475
return <div className={b('fill-bar', mods)} style={{width: '100%'}} />;
6576
}
6677

78+
if (!hasAllocatedPercent) {
79+
return null;
80+
}
81+
6782
// diskAllocatedPercent could be more than 100
6883
let fillWidth = Math.min(diskAllocatedPercent, 100);
6984
if (inverted) {
7085
fillWidth = Math.max(100 - diskAllocatedPercent, 0);
7186
}
7287

73-
if (diskAllocatedPercent >= 0) {
74-
return <div className={b('fill-bar', mods)} style={{width: `${fillWidth}%`}} />;
75-
}
76-
77-
return null;
88+
return <div className={b('fill-bar', mods)} style={{width: `${fillWidth}%`}} />;
7889
};
7990

8091
const renderContent = () => {
8192
if (content) {
8293
return content;
8394
}
8495

85-
if (!compact && diskAllocatedPercent >= 0) {
96+
if (!compact && hasAllocatedPercent) {
8697
return <div className={b('title')}>{`${Math.floor(diskAllocatedPercent)}%`}</div>;
8798
}
8899

100+
if (!compact && !hasAllocatedPercent && noDataPlaceholder) {
101+
return <div className={b('title')}>{noDataPlaceholder}</div>;
102+
}
103+
89104
return null;
90105
};
91106

@@ -111,7 +126,7 @@ export function DiskStateProgressBar({
111126
aria-label="Disk allocated space"
112127
aria-valuemin={0}
113128
aria-valuemax={100}
114-
aria-valuenow={diskAllocatedPercent}
129+
aria-valuenow={hasAllocatedPercent ? diskAllocatedPercent : undefined}
115130
>
116131
{iconElement}
117132
{renderAllocatedPercent()}

src/components/HoverPopup/HoverPopup.tsx

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,44 +34,47 @@ export const HoverPopup = ({
3434
delayOpen = DEBOUNCE_TIMEOUT,
3535
}: HoverPopupProps) => {
3636
const [isPopupVisible, setIsPopupVisible] = React.useState(false);
37-
const anchor = React.useRef<HTMLDivElement>(null);
37+
const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false);
38+
const [isFocused, setIsFocused] = React.useState(false);
39+
40+
const anchor = React.useRef<HTMLSpanElement>(null);
3841

3942
const debouncedHandleShowPopup = React.useMemo(
4043
() =>
4144
debounce(() => {
4245
setIsPopupVisible(true);
43-
onShowPopup?.();
4446
}, delayOpen),
45-
[onShowPopup, delayOpen],
47+
[delayOpen],
4648
);
4749

4850
const hidePopup = React.useCallback(() => {
4951
setIsPopupVisible(false);
50-
onHidePopup?.();
51-
}, [onHidePopup]);
52+
}, []);
5253

5354
const debouncedHandleHidePopup = React.useMemo(
5455
() => debounce(hidePopup, delayClose),
5556
[hidePopup, delayClose],
5657
);
5758

58-
const onMouseEnter = debouncedHandleShowPopup;
59+
const onMouseEnter = () => {
60+
debouncedHandleHidePopup.cancel();
61+
debouncedHandleShowPopup();
62+
};
5963

6064
const onMouseLeave = () => {
6165
debouncedHandleShowPopup.cancel();
6266
debouncedHandleHidePopup();
6367
};
6468

65-
const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false);
66-
const [isFocused, setIsFocused] = React.useState(false);
67-
6869
const onPopupMouseEnter = React.useCallback(() => {
6970
setIsPopupContentHovered(true);
70-
}, []);
71+
debouncedHandleHidePopup.cancel();
72+
}, [debouncedHandleHidePopup]);
7173

7274
const onPopupMouseLeave = React.useCallback(() => {
7375
setIsPopupContentHovered(false);
74-
}, []);
76+
debouncedHandleHidePopup();
77+
}, [debouncedHandleHidePopup]);
7578

7679
const onPopupContextMenu = React.useCallback(() => {
7780
setIsFocused(true);
@@ -87,16 +90,39 @@ export const HoverPopup = ({
8790
hidePopup();
8891
}, [hidePopup]);
8992

90-
const open = isPopupVisible || showPopup || isPopupContentHovered || isFocused;
93+
const internalOpen = isPopupVisible || isPopupContentHovered || isFocused;
94+
const open = internalOpen || showPopup;
95+
96+
const prevInternalOpenRef = React.useRef(internalOpen);
97+
98+
React.useEffect(() => {
99+
const prev = prevInternalOpenRef.current;
100+
101+
if (prev === internalOpen) {
102+
return;
103+
}
104+
105+
if (internalOpen) {
106+
onShowPopup?.();
107+
} else {
108+
onHidePopup?.();
109+
}
110+
111+
prevInternalOpenRef.current = internalOpen;
112+
}, [internalOpen, onShowPopup, onHidePopup]);
113+
114+
// Do not render Popup until it is available
115+
// to avoid a brief initial render at (0, 0) before positioning is applied.
116+
const anchorElement = anchorRef?.current || anchor.current;
91117

92118
return (
93119
<React.Fragment>
94120
<span ref={anchor} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
95121
{children}
96122
</span>
97-
{open ? (
123+
{open && anchorElement ? (
98124
<Popup
99-
anchorElement={anchorRef?.current || anchor.current}
125+
anchorElement={anchorElement}
100126
onOpenChange={(_open, _event, reason) => {
101127
if (reason === 'escape-key') {
102128
onPopupEscapeKeyDown();

src/components/VDisk/VDisk.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface VDiskProps {
2222
delayOpen?: number;
2323
delayClose?: number;
2424
withIcon?: boolean;
25+
highlighted?: boolean;
26+
darkened?: boolean;
2527
}
2628

2729
export const VDisk = ({
@@ -35,13 +37,15 @@ export const VDisk = ({
3537
delayClose,
3638
delayOpen,
3739
withIcon,
40+
highlighted,
41+
darkened,
3842
}: VDiskProps) => {
3943
const getVDiskLink = useVDiskPagePath();
4044
const vDiskPath = getVDiskLink({nodeId: data.NodeId, vDiskId: data.StringifiedId});
4145

4246
const severity = data.Severity;
4347
const isReplicatingColor = severity === DISK_COLOR_STATE_TO_NUMERIC_SEVERITY.Blue;
44-
const isHealthyDonor = data.DonorMode && isReplicatingColor;
48+
const isDonor = data.DonorMode;
4549

4650
return (
4751
<HoverPopup
@@ -60,10 +64,12 @@ export const VDisk = ({
6064
severity={severity}
6165
compact={compact}
6266
inactive={inactive}
63-
striped={isReplicatingColor}
64-
isDonor={isHealthyDonor}
67+
striped={isReplicatingColor || isDonor}
68+
isDonor={isDonor}
6569
className={progressBarClassName}
6670
withIcon={withIcon}
71+
highlighted={highlighted}
72+
darkened={darkened}
6773
/>
6874
</InternalLink>
6975
</div>

0 commit comments

Comments
 (0)